Name | Modified | Size | Downloads / Week |
---|---|---|---|
upload_with_progress.tgz | 2009-11-24 | 11.0 kB | |
README | 2009-11-23 | 7.0 kB | |
Totals: 2 Items | 18.1 kB | 0 |
Introduction ============ This patch is an implementation of an upload progress system based on nginx_uploadprogress_module source code(v 0.6), that monitors RFC1867 POST upload as they are transmitted to nginx_upload_module(v 2.0.10). Read more about nginx_uploadprogress_module: http://wiki.nginx.org/NginxHttpUploadProgressModule Read more about nginx_upload_module: http://www.grid.net.ru/nginx/upload.ru.html http://www.grid.net.ru/nginx/upload.en.html Installation ============ Download the Nginx upload module(v 2.0.10) sources from http://www.grid.net.ru/nginx/upload.ru.html and unpack it. Copy progress.c, progress.h, upload_with_progress.patch into nginx upload module directory. Apply upload_with_progress.patch : $ cd nginx_upload_module-2.0.10 $ patch < upload_with_progress.patch Nginx upload module installation instructions: http://www.grid.net.ru/nginx/upload.ru.html Configuration ============= Each upload request should be assigned a unique identifier. This unique identifier will be used to store the request and reference it to report. This identifier can be transmitted either as a GET argument or as an HTTP header whose name is X-Progress-ID. upload_progress +++++++++++++++ :Syntax: upload_progress <zone_name> <zone_size> :Default: none :Context: http :Description: This directive enables the upload progress module and reserve <zone_size> bytes to the <zone_name> which will be used to store the per-connection tracking information. track_uploads +++++++++++++ :Syntax: track_uploads <zone_name> <timeout> :Default: none :Context: location :Description: This directive enables tracking uploads for the current location. Each POST landing in this location will register the request in the <zone_name> upload progress tracker. The POST _must_ have a query parameter called X-Progress-ID (or an HTTP header of the same name) whose value is the unique identifier used to get progress information. If the POST has no such information, the upload will not be tracked. The tracked connections are kept at most <timeout> seconds after they have been finished to be able to serve unseful information to upload progress probes. report_uploads ++++++++++++++ :Syntax: report_uploads <zone_name> :Default: none :Context: location :Description: This directive allows a location to report the upload progress that is tracked by track_uploads for <zone_name>. The returned document is a JSON text with the possible 4 results: * the upload request hasn't been registered yet or is unknown: new Object({ 'state' : 'starting' }) * the upload request has ended: new Object({ 'state' : 'done' }) * the upload request generated an HTTP error new Object({ 'state' : 'error', 'status' : <error code> }) one error code that can be of use to track for the client is 413 (request entity too large). * the upload request is in progress: new Object({ 'state' : 'uploading', 'received' : <size_received>, 'size' : <total_size>}) The HTTP request to this location must have a X-Progress-ID parameter or HTTP header containing a valid unique identifier of an inprogress upload. Configuration Example: +++++++++++++++++++++ http { include mime.types; default_type application/octet-stream; # reserve 1MB under the name 'proxied' to track uploads upload_progress proxied 1m; server { listen 80; client_max_body_size 100m; # Upload form should be submitted to this location location /upload { # Pass altered request body to this location upload_pass @test; # track uploads in the 'proxied' zone # uploads expires 30s after they finish. track_uploads proxied 30s; # Store files to this directory # The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist upload_store /tmp 1; # Allow uploaded files to be read only by user upload_store_access user:r; # Set specified fields in request body upload_set_form_field "${upload_field_name}_name" $upload_file_name; upload_set_form_field "${upload_field_name}_content_type" $upload_content_type; upload_set_form_field "${upload_field_name}_path" $upload_tmp_path; # Inform backend about hash and size of a file upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5; upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size; upload_pass_form_field "^submit$|^description$"; } # Pass altered request body to a backend location @test { proxy_pass http://localhost:8080; } location /progress { # report uploads tracked in the 'proxied' zone report_uploads proxied; } } } Usage Example ============= (based on Lighttd mod_uploadprogress module example): First we need a upload form: <form id="upload" enctype="multipart/form-data" action="/upload.php" method="post" onsubmit="openProgressBar(); return true;"> <input type="hidden" name="MAX_FILE_SIZE" value="30000000" /> <input name="userfile" type="file" label="fileupload" /> <input type="submit" value="Send File" /> </form> And a progress bar to visualize the progress: <div> <div id="progress" style="width: 400px; border: 1px solid black"> <div id="progressbar" style="width: 1px; background-color: black; border: 1px solid white"> </div> </div> <div id="tp">(progress)</div> </div> Then we need to generate the Unique Identifier and launch the upload on submit action. This also will start the ajax progress report mechanism. interval = null; function openProgressBar() { /* generate random progress-id */ uuid = ""; for (i = 0; i < 32; i++) { uuid += Math.floor(Math.random() * 16).toString(16); } /* patch the form-action tag to include the progress-id */ document.getElementById("upload").action="/upload.php?X-Progress-ID=" + uuid; /* call the progress-updater every 1000ms */ interval = window.setInterval( function () { fetch(uuid); }, 1000 ); } function fetch(uuid) { req = new XMLHttpRequest(); req.open("GET", "/progress", 1); req.setRequestHeader("X-Progress-ID", uuid); req.onreadystatechange = function () { if (req.readyState == 4) { if (req.status == 200) { /* poor-man JSON parser */ var upload = eval(req.responseText); document.getElementById('tp').innerHTML = upload.state; /* change the width if the inner progress-bar */ if (upload.state == 'done' || upload.state == 'uploading') { bar = document.getElementById('progressbar'); w = 400 * upload.received / upload.size; bar.style.width = w + 'px'; } /* we are done, stop the interval */ if (upload.state == 'done') { window.clearTimeout(interval); } } } } req.send(null); }