NGINX アップロード進捗モジュール

nginx_uploadprogress_module - upstreamサーバに転送する時に RFC1867 POST アップロードを監視する、アップロード進捗システムの実装です。

アップロードされた内容を解析すること無しに、NGINXによってupstreamサーバにプロキシされるアップロードを追跡することで動作し、JavaScript、JSON、あるいは設定可能な形式でアップロードの進捗を報告するweb APIを提供します。NGINXはアップロードされたPOSTの内容をupstreamサーバに転送する前にディスクに格納し、upstreamサーバのアクセラレータとして振舞うため、このように動作します。個々の各POSTアップロードリクエストは進捗のユニークな識別子を持つべきです。

JSONと仕組みの考えは、Lighttpd の mod_uploadprogressに基づいています。

注意

このモジュールはNGINXのソースと一緒に配布されません。

ダウンロード

ダウンロードバージョン:

v0.8.4

ダウンロードバージョン:

v0.9.0

ソースコード:

masterzen/nginx-upload-progress-module

警告

バージョン 0.9.0 では、非互換の変更があります: JSONPは今では進捗の調査のデフォルトの出力です。もし、以下のように非推奨のjava出力を提供するこのモジュールに頼る場合は:

upload_progress_java_output

進捗調査locationの中で。

インストール

解凍した後で、以下のオプションをNGINX ./configure コマンドに追加します。

--add-module=path/to/nginx_uploadprogress_module

警告

--with-debugを使ってコンパイルされた場合、このモジュールはかなり多くのログメッセージを生成するでしょう。

ディレクティブ

upload_progress

構文:upload_progress
デフォルト:n/a
コンテキスト:http

このディレクティブはアップロード進捗モジュールを有効にし、接続ごとの追跡情報を格納するために使われるzone_namezone_size バイトを予約します。

track_uploads

構文:track_uploads
デフォルト:n/a
コンテキスト:場所

このディレクティブは現在のlocationのためのアップロードの追跡を有効にします。このlocationへのPOSTの到着はzone_name アップロード進捗トラッカー内にリクエストを登録するでしょう。NGINXはまだ RFC 1867 アップロードをサポートしないため、locationは proxy_pass あるいは fastcgi location でなければなりません。POSTは、進捗情報を取得するために使われるユニークな識別子を値に持つX-Progress-IDと呼ばれるクエリパラメータ (あるいは同じ名前の HTTP ヘッダ)を持たなければなりません。POSTがそのような情報を持たない場合、アップロードは追跡されないでしょう。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.

警告

このディレクティブはlocationの最後のディレクティブでなければなりません。proxy_pass あるいは fastcgi_pass locationの中になければなりません。location内でのディレクティブの繰り返しはsegfaultsに繋がるでしょう。

report_uploads

構文:report_uploads
デフォルト:n/a
コンテキスト:場所

このディレクティブによりlocationはzone_nameについて track_uploadsによって追跡されるアップロード進捗を報告することができます。返されるドキュメントはデフォルトで4つの結果を取り得るJavascriptテキストです:

  • アップロードリクエストはまだ登録されていないか、unknownです:

    new Object({ 'state' : 'starting' })
    
  • アップロードリクエストが終了しました:

    new Object({ 'state' : 'done' })
    
  • アップロードリクエストがHTTPエラーを起こしました

    new Object({ 'state' : 'error', 'status' : <error code> })
    

    クライアントのために追跡に使うことができる1つのエラーコードが 413 (request entity too large) です。

  • アップロードリクエストは進行中です:

    new Object({ 'state' : 'uploading', 'received' : <size_received>, 'size' : <total_size>})
    

このjavascriptの代わりにピュアなjsonを返すことができます (upload_progress_json_outputを見てください)。upload_progress_templateディレクティブを使って応答フォーマットを完全に設定することも可能です。

このlocationへのHTTPリクエストはX-Progress-IDパラメータを持つか、進捗中のアップロードの有効なユニークな識別子が含まれるHTTPヘッダを持つ必要があります。

upload_progress_content_type

構文:upload_progress_content_type
デフォルト:test/javascript
コンテキスト:場所

このディレクティブを使ってアップロード進捗調査content-typeを変更することができます。

upload_progress_header

構文:upload_progress_header
デフォルト:X-Progress-ID
コンテキスト:場所

このディレクティブを使って進捗IDのヘッダー名を変更することができます。

upload_progress_jsonp_parameter

構文:upload_progress_jsonp_parameter
デフォルト:callback
コンテキスト:場所

このディレクティブを使って、jsonpコールバック名を持つGETパラメータの名前を変更することができます。

upload_progress_json_output

構文:upload_progress_json_output
デフォルト:n/a
コンテキスト:main,sever,location

このディレクティブはピュアなjsonとして全てを出力するように設定します。

upload_progress_jsonp_output

構文:upload_progress_jsonp_output
デフォルト:none
コンテキスト:場所

このディレクティブはjsonpとして全てを出力するように設定します(json出力だがcallbackをf持たない)。

upload_progress_template

構文:upload_progress_template
デフォルト:n/a
コンテキスト:場所

このディレクティブは進捗応答テンプレートをインストールするために使うことができます。利用可能な状態のリスト:

  • starting
  • uploading
  • error
  • done

NGINXは以下の変数の値をアップロードについてのそれぞれの値に置き換えるでしょう:

  • $uploadprogress_length: アップロードの総サイズ
  • $uploadprogress_received: サーバが今までに受信したもの
  • $uploadprogress_status: HTTPエラーの場合のエラーコード
  • $uploadprogress_callback: 名前callbackを使ってGETクエリパラメータとしてjsonp コールバック名が渡された場合

例えば、(デフォルトのJavascriptあるいはjsonの代わりに)XMLを返すには:

upload_progress_content_type 'text/xml';
upload_progress_template starting '<upload><state>starting</state></upload>';
upload_progress_template uploading '<upload><state>uploading</state>
<size>$uploadprogress_length</size><uploaded>$uploadprogress_received</uploaded></upload>';
upload_progress_template done '<upload><state>done</state></upload>';
upload_progress_template error '<upload><state>error</state>
<syntaxhighlight>$uploadprogress_status</syntaxhighlight></upload>';

jsonp応答の例:

upload_progress_template starting "$uploadprogress_callback({ 'state' : 'starting'});";
upload_progress_template error "$uploadprogress_callback({ 'state' : 'error',
'status' : $uploadprogress_status });";
upload_progress_template done "$uploadprogress_callback({ 'state' : 'done'});";
upload_progress_template uploading "$uploadprogress_callback({ 'state' : 'uploading',
'received' : $uploadprogress_received, 'size' : $uploadprogress_length });";

設定例

http {
    # reserve 1MB under the name 'proxied' to track uploads
    upload_progress proxied 1m;

    server {
        listen       127.0.0.1 default;
        server_name  localhost;

        root /path/to/root;

        location / {
            # proxy to upstream server
            proxy_pass http://127.0.0.1;
            proxy_redirect default;

            # track uploads in the 'proxied' zone
            # remember connections for 30s after they finished
            track_uploads proxied 30s;
        }

        location ^~ /progress {
            # report uploads tracked in the 'proxied' zone
            report_uploads proxied;
        }
    }
}

ピュアなJavaScriptの使い方

(Lighttd mod_uploadprogress もジュールの例に基づいています):

まず、アップロードフォームが必要です:

<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>

進捗を表示するための進捗バー:

<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>

そして、ユニークな識別子を生成し、submitアクション上でアップロードを起動する必要があります。これはajax進捗レポート機構も開始するでしょう。

 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);
}

jQuery アップロード進捗の使い方

NGINX upload-progress-module と一緒にjQueryプラグインを使うことができます - https://github.com/drogus/jquery-upload-progress (そのドキュメントに基づいています)。

htmlの一部:

<form id="upload" enctype="multipart/form-data" action="index.html" method="post">
  <input name="file" type="file"/>
  <input type="submit" value="Upload"/>
</form>

<div id="uploading">
  <div id="progress" class="bar">
    <div id="progressbar">&nbsp;</div>
    <div id="percents"></div>
  </div>
</div>

cssの一部:

.bar {
  width: 300px;
}

#progress {
  background: #eee;
  border: 1px solid #222;
  margin-top: 20px;
}

#progressbar {
  width: 0px;
  height: 24px;
  background: #333;
}

少しのjavascript

$(function() {
  $('form').uploadProgress({
    /* scripts locations for safari */
    jqueryPath: "../lib/jquery.js",
    uploadProgressPath: "../jquery.uploadProgress.js",

    /* function called each time bar is updated */
    uploading: function(upload) {$('#percents').html(upload.percents+'%');},

    /* selector or element that will be updated */
    progressBar: "#progressbar",

    /* progress reports url */
    progressUrl: "/progress",

    /* how often will bar be updated */
    interval: 2000
  });
});

アップロードサーバが通常のwebサーバと違う場合など、異なるドメインあるいはサブドメイン(クロスドメイン)から進捗バーを更新する必要がある場合は、以下のようにJSONPプロトコルを試すことができます:

$(function() {
  $('form').uploadProgress({
    /* scripts locations for safari */
    jqueryPath: "../lib/jquery.js",
    uploadProgressPath: "../jquery.uploadProgress.js",

    /* function called each time bar is updated */
    uploading: function(upload) {$('#percents').html(upload.percents+'%');},

    /* selector or element that will be updated */
    progressBar: "#progressbar",

    /* progress reports url in a different domain or subdomain from caller */
    progressUrl: "uploads.somewhere.com/progress",

    /* how often will bar be updated */
    interval: 2000,

    /* use json-p for cross-domain call */
    dataType: 'jsonp'
  });
});

デフォルト:

  • interval: 2000
  • progressBar: “#progressbar”
  • progressUrl: “/progress”
  • start: function() {}
  • uploading: function() {}
  • complete: function() {}
  • success: function() {}
  • error: function() {}
  • uploadProgressPath: ‘/javascripts/jquery.js’
  • jqueryPath: ‘/javascripts/jquery.uploadProgress.js’
  • dataType: ‘json’

ソフトウェア比較

このソフトウェアは Valery Kholodkov の NGINX アップロードモジュールとも動作します: http://www.grid.net.ru/nginx/upload.en.html

以下のクライアント側のjavascriptライブラリも使うことができます: http://drogomir.com/blog/2008/6/30/upload-progress-script-with-safari-support