ファイルアップロード時のプログレスイベント
前回投稿したファイルアップロード処理について、ファイル容量が大きい場合に
アップロードの状態を把握する為にプログラムイベントを使って、現在何パーセントの状態かを取得する方法を試します。
アップロード部分の処理は、前回までと同様の処理を書きますが、
ファイルの送信状況を把握する為のイベントとして、次のイベントを使います。
仕組みは、送信処理時(sendした時)にloadstartイベントが発火され、その後にprogressイベントが発生します。
このprogressイベントを使うことにより、ファイルの送信状況を画面に表示することができます。
また、通信の終わりには、XMLHttpRequestオブジェクトのstatusコードを判定し、
値が200の場合には正常に終了した。という送信状態を把握します。
通信時のエラーチェックについて
通信時になんらかの異常があってエラーになる場合があります。
一つはリクエストがタイムアウトした場合、二つ目はリクエストが中止された場合、三つ目はネットワークエラー等でリクエストが完了できない場合、です。
loadstartとloadendについて
さきほど「送信処理時(sendした時)にloadstartイベントが発火され」と書きましたが、
通信処理が終了した際には、ブラウザがload、abort、timeout、errorのイベントを発生させます。
この際、XHR2仕様ではloadendイベントが発生すると定義づけられています。
また、このイベントは、通信処理が成功しても失敗しても発生します。
通信時のXMLHttpRequestオブジェクトについて
通信時のXMLHttpRequestオブジェクトにイベントをaddEventListenerでメソッドを付与し、
プログレスイベント「ProgressEvent(インターフェイス)」にハンドラを登録することができます。
このハンドラに対し、onprogressやonload等のプロパティを設定する方法があります。
このプロパティにはtotal、lengthComputable、loaded、等のプロパティがあります。
それぞれ次のような意味です。
lengthComputable 通信時の進捗が測定可能かどうかを返す(true / false) loaded 現時点までに転送されたバイト数(64ビット符号なし整数値)が返されます total 転送するデータの総容量が格納されています。不明な場合は0です。
これらのプロパティを使用し、ファイル転送中の進捗状況を把握することができます。
アップロード処理の実例
実際に送信状態を把握するアップロード処理を確認してみます。
ファイルをアップロードする際、uploadプロパティが使用できます。
また、ProgressEventのプロパティとして、onprogressやonload等のプロパティも定義されています。
下記のようなHTMLを用意しました。
サーバ側のプログラムは、前回と同様の為ここでは記載を省きます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <form id="testform" enctype="multipart/form-data"> <div> <input type="file" name="file1" id="file1"> </div> <div> <label for="progress">進捗</label> <div id="percent"></div> <progress id="progress" value="0" max="100"></progress> </div> <div> <input type="button" id="sample_file_upload" value="ファイルアップロードのテスト"> </div> </form> <div>通信時の処理内容</div> <div id="ajax_result"></div> <script type="text/javascript"> // ファイルアップロードボタンのDOMを取得 let element_sample_file_upload = document.getElementById('sample_file_upload'); element_sample_file_upload.addEventListener('click', SendXMLHttpRequest, false); // ボタン押下時の処理 function SendXMLHttpRequest() { // 通信処理を画面に出力する為、操作用DOMの取得 let result = document.getElementById("ajax_result"); result.innerHTML += 'ファイルアップロードのテスト start' + '<br />'; result.innerHTML += '通信処理の開始' + '<br />'; // フォーム内容の取得 let testform_data = document.getElementById("testform"); // 送信用データ let form_data = new FormData(testform_data); // 通信用XMLHttpRequestを生成 let req = new XMLHttpRequest(); // POST形式でサーバ側の「response.php」へデータ通信を行う req.open("POST", "response.php"); // ファイル通信時の進捗確認用にプログレスバーを更新する req.upload.addEventListener("progress", function(e) { if (e.lengthComputable) { let progress_value = Math.round(e.loaded / e.total * 100); // 進捗%表示用に値を更新する document.getElementById('percent').innerHTML = progress_value + "%"; // プログレスバーの値にファイル転送値を代入する document.getElementById('progress').value = progress_value; // 進捗率が100%になったら終了の処理をする if (progress_value == 100) { // 通信が完了したらレスポンスをコンソールに出力する req.addEventListener('readystatechange', () => { // ここでレスポンス結果を制御する console.log("レスポンス結果"); }); result.innerHTML += '通信処理の終了' + '<br />'; result.innerHTML += 'レファイルアップロードのテスト end' + '<br />'; } } }); // ファイルが選択されたときに処理を実行するようイベントリスナーに登録 input_file = document.getElementById("file1"); input_file.addEventListener('change', function(e) { form_data.append('file1', e.target.files[0]); }); // ファイル送信 req.send(form_data); } </script> </body> </html>
画面にアクセスすると、まず次のような画面が表示されます。
次に、ファイルを選択します。
「ファイルアップロードのテスト」のボタンを押すと、ファイルのアップロードが開始されます。
この時、アップロードの進捗状態に応じて、プログレスバーの値が更新されていきます。
(同時に画面に進捗率を表示しています)
ファイルのアップロードが完了すると、プログレスバーは100%になり、処理が終わります。
実際には、100%になった後に、ユーザにはなんらかのアクションを求めるので、
継続的な操作をするUIにしたほうが、わかりやすく親切です。
アップロード処理のポイントと注意点
アップロード処理をする際のポイントとなる点は、
// 通信用XMLHttpRequestを生成 let req = new XMLHttpRequest();
で、XMLHttpRequestオブジェクト(ここでいうインスタンスのreqです)を生成した後、
reqに対して、progressイベントをaddEventListenerしている箇所です。
// ファイル通信時の進捗確認用にプログレスバーを更新する req.upload.addEventListener("progress", function(e) { if (e.lengthComputable) { let progress_value = Math.round(e.loaded / e.total * 100); // 進捗%表示用に値を更新する document.getElementById('percent').innerHTML = progress_value + "%"; // プログレスバーの値にファイル転送値を代入する document.getElementById('progress').value = progress_value; } });
それ以外は、前回ファイルのアップロードを投稿したサンプルと同じ処理にしています。
また、注意点として、実際に大きい容量のファイルをアップロードして試してみるとわかりますが
サーバ側で受信できない容量サイズのファイルは、javascript側でアップロード完了までの
プログレスバーの表示になりますが、サーバ側でファイル受信(容量制限)ができていないケースがあります。
HTML+css+javascriptの作りとして、見かけ上は処理が止まっていないので、
ファイルがアップロード成功したかのように見えますが、サーバサイドで正常にファイルアップロードが
成功したかどうかは、サーバ側プログラムで判断し、容量オーバやなんからのエラーだった場合には
クライアント(javascript)側にエラーのレスポンスを返す必要があります。