XMLHttpRequestから進捗状況を取得する方法


133

XMLHttpRequestの進行状況(アップロードされたバイト数、ダウンロードされたバイト数)を取得することは可能ですか?

これは、ユーザーが大きなファイルをアップロードしているときに進行状況バーを表示するのに役立ちます。標準APIはそれをサポートしていないようですが、おそらく、そこにあるブラウザーのいずれかに非標準の拡張機能があるのでしょうか?クライアントはアップロード/ダウンロードされたバイト数を知っているので、それは結局のところかなり明白な機能のようです。

注:私は「進行状況をサーバーにポーリングする」代替案を知っています(これは私が今行っていることです)。これの主な問題は(複雑なサーバー側コードを除く)、通常、大きなファイルをアップロードしている間、ほとんどのISPはアップストリームの品質が悪いため、ユーザーの接続が完全に解放されます。したがって、追加のリクエストを行うことは、私が期待したほど応答性がよくありません。私は、ブラウザが常に持っているこの情報を取得する方法(おそらく非標準)があることを望んでいました。

回答:


137

アップロードされたバイト数については、非常に簡単です。xhr.upload.onprogressイベントを監視するだけです。ブラウザーは、アップロードする必要があるファイルのサイズとアップロードされたデータのサイズを知っているので、進行状況を提供できます。

ダウンロードされたバイト数(で情報を取得する場合)の場合xhr.responseText、サーバー要求で送信されるバイト数がブラウザーに認識されないため、少し難しいです。この場合、ブラウザが認識するのは、受信しているバイトのサイズだけです。

これには解決策がありContent-Lengthます。ブラウザーが受信するバイトの合計サイズを取得するには、サーバースクリプトにヘッダーを設定するだけで十分です。

詳細については、https://developer.mozilla.org/en/Using_XMLHttpRequestにアクセスしてください

例:サーバースクリプトがzipファイルを読み取る(5秒かかります):

$filesize=filesize('test.zip');

header("Content-Length: " . $filesize); // set header length
// if the headers is not set then the evt.loaded will be 0
readfile('test.zip');
exit 0;

これで、サーバースクリプトのダウンロードプロセスを監視できます。これは、全体の長さがわかっているためです。

function updateProgress(evt) 
{
   if (evt.lengthComputable) 
   {  // evt.loaded the bytes the browser received
      // evt.total the total bytes set by the header
      // jQuery UI progress bar to show the progress on screen
     var percentComplete = (evt.loaded / evt.total) * 100;  
     $('#progressbar').progressbar( "option", "value", percentComplete );
   } 
}   
function sendreq(evt) 
{  
    var req = new XMLHttpRequest(); 
    $('#progressbar').progressbar();    
    req.onprogress = updateProgress;
    req.open('GET', 'test.php', true);  
    req.onreadystatechange = function (aEvt) {  
        if (req.readyState == 4) 
        {  
             //run any callback here
        }  
    };  
    req.send(); 
}

29
注目に値するのは、「Content-Length」は推定の長さではなく、正確な長さである必要があり、短すぎるとブラウザがダウンロードを中断し、長すぎるとブラウザはダウンロードの完了に失敗したと想定します。
Chris Chilvers 2012

@ChrisChilversつまり、PHPファイルが正しく計算されない可能性がありますよね?
Hydroper

1
その例では@nicemattはファイルから取得されるので問題ありませんが、メモリから直接zipをストリーミングしている場合は、コンテンツの長さを構成することも、推定することもできません。
Chris Chilvers

3
@TheProHands .phpページにアクセスすると、サーバーはPHPファイルを実行し、その出力を送信します。サーバーは、.phpファイルではなく、出力の長さを送信する必要があります。
leewz

誰かが "$( '#progressbar')。progressbar(" option "、" value "、percentComplete);"という行を説明できますか?手段?これは特定のプラグインを呼び出していますか?これはどのように作動しますか?新しいユーザーが理解できるようにしてください。
ザック




5

アップロードされた合計に対してそれを処理する方法はないようですが、ダウンロードしたいものに似たものがあります。readyStateが3になったら、responseTextを定期的にクエリして、すべてのコンテンツを文字列としてダウンロードします(これはIEでは機能しません)。すべてのコンテンツが利用可能になり、その時点でreadyState 4に移行します。ある時点でダウンロードされたバイト数は、responseTextに格納されている文字列の合計バイト数と等しくなります。

アップロードの質問に対するオールオアナッシングアプローチの場合、アップロード用の文字列を渡す必要があるため(そしてその合計バイト数を決定することが可能です)、readyState 0および1に送信される合計バイト数は0になり、readyStateの合計数は0になります2は、渡した文字列の合計バイト数です。readyState3と4で送受信された合計バイト数は、元の文字列のバイト数とresponseTextの合計バイト数の合計になります。


3

<!DOCTYPE html>
<html>
<body>
<p id="demo">result</p>
<button type="button" onclick="get_post_ajax();">Change Content</button>
<script type="text/javascript">
	function update_progress(e)
	{
	  if (e.lengthComputable)
	  {
	    var percentage = Math.round((e.loaded/e.total)*100);
	    console.log("percent " + percentage + '%' );
	  }
	  else 
	  {
	  	console.log("Unable to compute progress information since the total size is unknown");
	  }
	}
	function transfer_complete(e){console.log("The transfer is complete.");}
	function transfer_failed(e){console.log("An error occurred while transferring the file.");}
	function transfer_canceled(e){console.log("The transfer has been canceled by the user.");}
	function get_post_ajax()
	{
	  	var xhttp;
	  	if (window.XMLHttpRequest){xhttp = new XMLHttpRequest();}//code for modern browsers} 
	 	else{xhttp = new ActiveXObject("Microsoft.XMLHTTP");}// code for IE6, IE5	  	
	  	xhttp.onprogress = update_progress;
		xhttp.addEventListener("load", transfer_complete, false);
		xhttp.addEventListener("error", transfer_failed, false);
		xhttp.addEventListener("abort", transfer_canceled, false);	  	
	  	xhttp.onreadystatechange = function()
	  	{
	    	if (xhttp.readyState == 4 && xhttp.status == 200)
	    	{
	      		document.getElementById("demo").innerHTML = xhttp.responseText;
	    	}
	  	};
	  xhttp.open("GET", "http://it-tu.com/ajax_test.php", true);
	  xhttp.send();
	}
</script>
</body>
</html>

結果


2

Apacheのインストールにアクセスしてサードパーティのコードを信頼できる場合は、Apacheのアップロード進行状況モジュールを使用できます(Apacheを使用している場合は、nginxのアップロード進行状況モジュールもあります)。

それ以外の場合は、帯域外にヒットしてファイルのステータスを要求できるスクリプトを作成する必要があります(たとえば、tmpファイルのファイルサイズを確認します)。

Firefox 3で進行中の作業がいくつかあります。ブラウザーにアップロードの進行状況のサポートを追加すると思いますが、それはすべてのブラウザーに適用され、しばらくの間広く採用されることはありません(もっと残念です)。


-7

純粋なJavaScriptでこれを行う唯一の方法は、ある種のポーリングメカニズムを実装することです。サーバーが受信したバイト数を取得するには、一定の間隔(たとえば5秒ごと)でajaxリクエストを送信する必要があります。

より効率的な方法は、フラッシュを使用することです。フレックスコンポーネントFileReferenceは、すでにアップロードされたバイト数を保持する「進行」イベントを定期的に送出します。javascriptを使い続ける必要がある場合は、actionscriptとjavascriptの間でブリッジを使用できます。良い知らせは、この作業はすでにあなたのために行われているということです:)

swfupload

このライブラリを使用すると、フラッシュの進行状況イベントでJavaScriptハンドラを登録できます。

このソリューションには、サーバー側に追加のリソースを必要としないという利点があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.