それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

$.ajaxファイルアップロードでプログレスバーを表示する。

速度制限が毎日続くTakuyaです。こんにちは!。

データセンターにmpeg動画を送信して、サーバー側でffmpeg 掛けてました。節電の為に自宅鯖を停止し、Linuxボックスだけ起動してました。重い処理はサーバーにやらせてました。ファイル送信が3TB超えてついに速度制限を喰らいました。怖いですね。11/01に無事解除されましたが。。。

<input type=file /> の通信状況を見たい。

UP速度制限が100kbpsでした、ブラウザのファイルアップロードに不自由を感じました。iphone4の写真UPに1枚あたり30秒位かかってました。さくらVPSのWEBアプリに写真をアップするだけでも大変でした。ファイル送信状況が見えないと不安しかたありません。< input type=file/> でアップしたファイルの送信状況を見れるようにフォームを改造しました。

$.ajax でHTMLフォームに プログレスバー表示した時の話を、エントリにまとめました。

送信状況のプログレスバーを作るには?

  • ajax で送信する
  • XmlHttpの progress イベントを使う。

この2つを満します。ファイルアップロードを作ることが出来ました。

ajax で送信する

ajax の ansychronous モードは必須です。async=trueで送れば、進捗が見られました。syncモード(async=false)では状況モニタリング出来ませんでした。。*1

progressイベントを使う

progressイベントのハンドラを登録すると使えます。つかいかたは、Mozillaのページが分かりやすい。

var req = new XMLHttpRequest();
 
req.addEventListener("progress", updateProgress, false);
req.addEventListener("load", transferComplete, false);
req.addEventListener("error", transferFailed, false);
req.addEventListener("abort", transferCanceled, false);
 
req.open();
https://developer.mozilla.org/ja/docs/DOM/XMLHttpRequest/Using_XMLHttpRequest:

コールバック関数で アップしたファイルサイズを見る

function updateProgress(evt) {
    var percentComplete = evt.loaded / evt.total;
}
event.total
アップ中ファイルのトータルサイズ
event.loaded
アップしたファイルサイズ

loaded とあるがこれが送信済みファイルサイズ。

jQueryでやりたい。

Ajaxを生でやるより、$.ajaxを使いたいですよね。わかります。僕もです。
xmlhttpを直接触るのは控えたい。jQueryを導入してるので jQueryでやりたい。

jqueryの$.jajaxに progressを使う
var start_upload = function(){ 
    $.each(
        files_field.files,
        function(i,e){
            var fd = new FormData();
            fd.append("filename", e );
            fd.append("dummy", "1234" );
            $.ajax({
                async: true,
                xhr : function(){
                                XHR = $.ajaxSettings.xhr();
                                if(XHR.upload){
		          XHR.upload.addEventListener('progress',function(e){
                                                                             progre = parseInt(e.loaded/e.total*10000)/100 ;
						console.log(progre+"%") ;
						$("#progress_bar").width(parseInt(progre/100*300*100)/100+"px");
						$("#progress_bar").height("30px");
						$("#progress_bar").html(progre+"%");
                                                                          }, false); 
	                     }
		return XHR;
	    },
                url:  "/test/",
                type: "post",
                data:fd,//formdataのオブジェクト
                contentType: false,
                processData: false
            }).done(function( msg ) { console.log( msg );$("#output").append(msg) });
        }
    )
}

出来上がりは上記のようになりました。


FileAPIとProgressを使ってる2箇所について抜き出して解説します。



大事な所:1

$.ajaxでプログレスイベントを拾う箇所

          $.ajax({
                async: true,
                xhr : function(){
                                     XHR = $.ajaxSettings.xhr();
                                     XHR.upload.addEventListener('progress',function(e){})
	                        return XHR;
	                       },
                type: "post",
                data:fd,//formdataのオブジェクト
                contentType: false,
                processData: false
          });

jqueryの $.ajax引数に { xhr:function(){} } を渡します。これが生の XHR書換えるの基本だと思います。

大事な箇所:2

$.ajaxでファイルをアップロードする ( multipart/form-dataを ajaxで送る )
jQueryでファイルアップロードには、手順が必要です

var fd = new FormData();
fd.append( $(":file").get(0).files.item(0), e );
$.ajax({
                type: "post",
                data:fd,//formdataのオブジェクト
                contentType: false,
                processData: false

});

詳しくは以前のエントリに→File APIその後使ってみて - ブックマクロ開発に

jquery特徴

<inpu type=file multiple=multiple>で複数ファイルを取得するには、次のようにします。

$.map( $(":file").get(0).files, function(e,i ){ console.log(e); return e })

最後に

jqueryを使わず、純粋にファイルの送信状況を出す部分

var req = new XMLHttpRequest();
req.upload.onprogress =function(e){
                                    console.log((e.loaded / e.total) * 100 + "%");
                                  }
req.open("POST", "/test/" );
req.send( e );


簡単ですね。websocketでやるのかなとおもったら xmlhttpに組み込まれてて安心した。

*1:sync/ansyncの差異はW3Cに記述がない。どちらでもモニタリング可能かとおもったが、現状は ansyncのみのようだ(2012-11-01)