Download Latest Version upload_with_progress.tgz (11.0 kB)
Email in envelope

Get an email when there's a new version of Nginx UploadProgress2 Module

Home
Name Modified Size InfoDownloads / 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">
     &nbsp;
    </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);
}






Source: README, updated 2009-11-23