README
1Nginx Upload Progress Module
2============================
3
4Introduction
5============
6
7nginx_uploadprogress_module is an implementation of an upload progress system, that monitors
8RFC1867 POST upload as they are transmitted to upstream servers.
9
10It works by tracking the uploads proxied by Nginx to upstream servers without
11analysing the uploaded content and offers a web API to report upload progress in Javscript, Json or any
12other format (through the help of templates).
13
14It works because Nginx acts as an accelerator of an upstream server, storing uploaded POST content
15on disk, before transmitting it to the upstream server. Each individual POST upload request
16should contain a progress unique identifier.
17
18This module is Copyright (c) 2007-2012 Brice Figureau, and is licensed under the BSD license (see LICENSE).
19 * rbtree and shm_zone code is based on Igor Sysoev limit_zone Nginx module.
20 * expire header code is based on Igor Sysoev header_filter Nginx module.
21
22The JSON idea and the mechanism idea are based on Lighttpd mod_uploadprogress:
23http://blog.lighttpd.net/articles/2006/08/01/mod_uploadprogress-is-back
24
25
26WARNING:
27 * when compiled with --with-debug, this module will produce high number of log messages.
28
29INCOMPATIBLE CHANGES
30====================
31
32v0.9.0:
33
34JSONP is now the default output of the progress probes. If you rely on this module serving
35the deprecated java output use:
36 upload_progress_java_output
37in the progress probe location.
38
39
40Installation
41============
42
43nginx_uploadprogress_module has been tested with Nginx 0.6.x, 0.7.x, 0.8.x and 1.0.x.
44
45Download the Nginx sources from http://nginx.net/ and unpack it.
46
47To build Nginx, change to the directory which contains the Nginx
48sources, and run the configuration script making sure to add the path
49to the nginx_uploadprogress_module sources using the --add-module option: ::
50
51 $ ./configure --add-module=/path/to/nginx_uploadprogress_module/
52
53Now you can build and install the software:
54
55 $ make
56
57and as root:
58
59 $ make install
60
61
62Configuration
63=============
64
65Each upload request should be assigned a unique identifier. This unique identifier will be used
66to store the request and reference it to report.
67This identifier can be transmitted either as a GET argument or as an HTTP header whose name is X-Progress-ID.
68
69upload_progress
70+++++++++++++++
71 :Syntax: upload_progress <zone_name> <zone_size>
72 :Default: none
73 :Context: http
74 :Description:
75 This directive enables the upload progress module and reserve <zone_size> bytes to the <zone_name> which
76 will be used to store the per-connection tracking information.
77
78track_uploads
79+++++++++++++
80 :Syntax: track_uploads <zone_name> <timeout>
81 :Default: none
82 :Context: location
83 :Description:
84 This directive enables tracking uploads for the current location. Each POST landing in this location will register
85 the request in the <zone_name> upload progress tracker.
86 Since Nginx doesn't support yet RFC 1867 upload, the location must be a proxy_pass or fastcgi location.
87 The POST _must_ have a query parameter called X-Progress-ID (or an HTTP header of the same name) whose value is the
88 unique identifier used to get progress information. If the POST has no such information, the upload will not be tracked.
89 The tracked connections are kept at most <timeout> seconds after they have been finished to be able to serve
90 useful information to upload progress probes.
91 WARNING: this directive must be the last directive of the location. It must be in a proxy_pass or
92 fastcgi_pass location.
93
94report_uploads
95++++++++++++++
96 :Syntax: report_uploads <zone_name>
97 :Default: none
98 :Context: location
99 :Description:
100 This directive allows a location to report the upload progress that is tracked by track_uploads for <zone_name>.
101 The returned document is a Javascript text with the possible 4 results by default:
102 * the upload request hasn't been registered yet or is unknown:
103 new Object({ 'state' : 'starting' })
104
105 * the upload request has ended:
106 new Object({ 'state' : 'done' })
107
108 * the upload request generated an HTTP error
109 new Object({ 'state' : 'error', 'status' : <error code> })
110 one error code that can be of use to track for the client is 413 (request entity too large).
111
112 * the upload request is in progress:
113 new Object({ 'state' : 'uploading', 'received' : <size_received>, 'size' : <total_size>})
114
115 It is possible to return pure json instead of this javascript (see upload_progress_json_output).
116 It is also possible to configure completely the response format with the directive:
117 upload_progress_template
118
119 The HTTP request to this location must have a X-Progress-ID parameter or HTTP header containing a valid
120 unique identifier of an in progress upload.
121
122upload_progress_content_type
123++++++++++++++++++++++++++++
124 :Syntax: upload_progress_content_type <content_type>
125 :Default: text/javascript
126 :Context: location
127 :Description:
128 This directive allows to change the upload progress probe response content-type.
129
130upload_progress_header
131++++++++++++++++++++++
132 :Syntax: upload_progress_header <progress-id>
133 :Default: X-Progress-ID
134 :Context: location
135 :Description:
136 This directive allows to change the header name of the progress ID.
137
138upload_progress_jsonp_parameter
139++++++++++++++++++++++
140 :Syntax: upload_progress_jsonp_parameter <callback_parameter>
141 :Default: callback
142 :Context: location
143 :Description:
144 This directive allows to change the name of the GET parameter with the jsonp callback name.
145
146upload_progress_java_output
147+++++++++++++++++++++++++++
148 :Syntax: upload_progress_java_output
149 :Default: N/A
150 :Context: location
151 :Description:
152 This directive sets everything to output as eval() javascript compatible code.
153
154upload_progress_json_output
155+++++++++++++++++++++++++++
156 :Syntax: upload_progress_json_output
157 :Default: N/A
158 :Context: location
159 :Description:
160 This directive sets everything to output as pure json.
161
162upload_progress_jsonp_output
163++++++++++++++++++++++++++++
164 :Syntax: upload_progress_jsonp_output
165 :Default: N/A
166 :Context: location
167 :Description:
168 This directive sets everything to output as jsonp (like json output, but with callback).
169
170upload_progress_template
171++++++++++++++++++++++++
172 :Syntax: upload_progress_template <state> <template>
173 :Default: none
174 :Context: location
175 :Description:
176 This directive can be used to install a progress response template.
177 The available list of state is:
178 * starting
179 * uploading
180 * error
181 * done
182
183 Nginx will replace the value of the following variables with their respective
184 value for the upload:
185 * $uploadprogress_length: total size of the upload
186 * $uploadprogress_received: what the server has received so far
187 * $uploadprogress_status: error code in case of HTTP error
188 * $uploadprogress_callback: jsonp callback name if provided as a GET query parameter with name 'callback'
189
190 For instance to return XML (instead of the default Javascript or json):
191
192 upload_progress_content_type 'text/xml';
193 upload_progress_template starting '<upload><state>starting</state></upload>';
194 upload_progress_template uploading '<upload><state>uploading</state><size>$uploadprogress_length</size><uploaded>$uploadprogress_received</uploaded></upload>';
195 upload_progress_template done '<upload><state>done</state></upload>';
196 upload_progress_template error '<upload><state>error</state><code>$uploadprogress_status</code></upload>';
197
198 Example of jsonp response:
199
200 upload_progress_template starting "$uploadprogress_callback({ \"state\" : \"starting\"});";
201 upload_progress_template error "$uploadprogress_callback({ \"state\" : \"error\", \"status\" : $uploadprogress_status });";
202 upload_progress_template done "$uploadprogress_callback({ \"state\" : \"done\"});";
203 upload_progress_template uploading "$uploadprogress_callback({ \"state\" : \"uploading\", \"received\" : $uploadprogress_received, \"size\" : $uploadprogress_length });";
204
205Configuration Example:
206+++++++++++++++++++++
207
208http {
209
210 # reserve 1MB under the name 'proxied' to track uploads
211 upload_progress proxied 1m;
212
213 server {
214 listen 127.0.0.1 default;
215 server_name _ *;
216
217 root /path/to/root;
218
219 location / {
220 # proxy to upstream server
221 proxy_pass http://127.0.0.1;
222 proxy_redirect default;
223
224 # track uploads in the 'proxied' zone
225 # remember connections for 30s after they finished
226 track_uploads proxied 30s;
227 }
228
229 location ^~ /progress {
230 # report uploads tracked in the 'proxied' zone
231 report_uploads proxied;
232 }
233}
234
235
236Usage Example
237=============
238
239(based on Lighttd mod_uploadprogress module example):
240
241First we need a upload form:
242
243 <form id="upload" enctype="multipart/form-data"
244 action="/upload.php" method="post"
245 onsubmit="openProgressBar(); return true;">
246 <input type="hidden" name="MAX_FILE_SIZE" value="30000000" />
247 <input name="userfile" type="file" label="fileupload" />
248 <input type="submit" value="Send File" />
249 </form>
250
251And a progress bar to visualize the progress:
252
253 <div>
254 <div id="progress" style="width: 400px; border: 1px solid black">
255 <div id="progressbar"
256 style="width: 1px; background-color: black; border: 1px solid white">
257
258 </div>
259 </div>
260 <div id="tp">(progress)</div>
261 </div>
262
263Then we need to generate the Unique Identifier and launch the upload on submit
264action. This also will start the ajax progress report mechanism.
265
266 interval = null;
267
268function openProgressBar() {
269 /* generate random progress-id */
270 uuid = "";
271 for (i = 0; i < 32; i++) {
272 uuid += Math.floor(Math.random() * 16).toString(16);
273 }
274 /* patch the form-action tag to include the progress-id */
275 document.getElementById("upload").action="/upload.php?X-Progress-ID=" + uuid;
276
277 /* call the progress-updater every 1000ms */
278 interval = window.setInterval(
279 function () {
280 fetch(uuid);
281 },
282 1000
283 );
284}
285
286function fetch(uuid) {
287 req = new XMLHttpRequest();
288 req.open("GET", "/progress", 1);
289 req.setRequestHeader("X-Progress-ID", uuid);
290 req.onreadystatechange = function () {
291 if (req.readyState == 4) {
292 if (req.status == 200) {
293 /* poor-man JSON parser */
294 var upload = eval(req.responseText);
295
296 document.getElementById('tp').innerHTML = upload.state;
297
298 /* change the width if the inner progress-bar */
299 if (upload.state == 'done' || upload.state == 'uploading') {
300 bar = document.getElementById('progressbar');
301 w = 400 * upload.received / upload.size;
302 bar.style.width = w + 'px';
303 }
304 /* we are done, stop the interval */
305 if (upload.state == 'done') {
306 window.clearTimeout(interval);
307 }
308 }
309 }
310 }
311 req.send(null);
312}
313
314Companion Software
315==================
316
317This software can also work with Valery Kholodkov' Nginx Upload Module:
318http://www.grid.net.ru/nginx/upload.en.html
319
320You can also use the following javascript libraries client side:
321http://drogomir.com/blog/2008/6/30/upload-progress-script-with-safari-support
322
323Note that when using jQuery AJAX for progress monitoring, such as:
324https://github.com/drogus/jquery-upload-progress
325you should be sure to set a upload_progress template parameter:
326upload_progress_json_output
327or
328upload_progress_jsonp_output
329depending on your jQuery AJAX dataType setting.
330