ファイルアップロード時のプログレスイベント

javascript

ファイルアップロード時のプログレスイベント

前回投稿したファイルアップロード処理について、ファイル容量が大きい場合に
アップロードの状態を把握する為にプログラムイベントを使って、現在何パーセントの状態かを取得する方法を試します。

アップロード部分の処理は、前回までと同様の処理を書きますが、
ファイルの送信状況を把握する為のイベントとして、次のイベントを使います。

仕組みは、送信処理時(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>

サーバ上のHTMLはこちら(test1.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)側にエラーのレスポンスを返す必要があります。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です