ajaxでマルチパートを使わずにファイルアップロードする方法

## 1. はじめに

今回はajaxでマルチパートを使わずにファイルをアップロードする方法について説明したいと思います。

**（ポイント）**

* JQueryなどの外部ライブラリは利用せず、素のJavaScriptのみで実現する
 * `XMLHttpRequest`と`File(Blob)`を利用する
* `multipart/form-data`ではなく、ファイル種別に応じた任意の`content-type`でデータを送信する
 * HTTPリクエストのBODYはファイルデータそのもの（バイナリ）となり、`multipart/form-data（たとえばbase64）`のエンコード、デコード処理が不要となる
 * サーバ側のアップロード処理がこの方法に対応している必要がある

## 2. ソースコード

```html:upload.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>upload</title>
</head>
<body>
	<div id="wrapper">
		<div>
			<input id="uploadFile" type="file" />
		</div>
		<div>
			<input id="uploadButton" type="button" value="送信" />
		</div>
		<div>
			<p id="message">ファイルを選択して「送信」ボタンを押してください。</p>
		</div>
		<div>
			<progress id="uploadProgress" max="100" value="0"></progress>
		</div>
	</div>
	<script type="text/javascript">
	<!--
		(function() {

			var post_file = function(upload_file) {
				var maeesage = document.getElementById("message");
				maeesage.innerHTML = "アップロード中です。";

				// ★ポイント1
				var content_length = upload_file.size
				var content_type = upload_file.type
				var file_name = upload_file.name;

				// ★ポイント2
				var xhr = new XMLHttpRequest();
				xhr.open('POST', '/todo-rest/upload/chunked', true);
				xhr.setRequestHeader('Content-type', content_type);
				xhr.setRequestHeader('Content-Length', content_length);
				xhr.setRequestHeader('X-FILE-NAME', file_name);

				// ★ポイント3
				xhr.onload = function(e) {
					if (this.readyState == 4) {
						maeesage.innerHTML = "アップロードが完了しました。";
					}
				};

				// ★ポイント4
				var progress = document.getElementById("uploadProgress");
				xhr.upload.onprogress = function(e) {
					if (e.lengthComputable) {
						progress.value = (e.loaded / e.total) * 100;
					}
				};

				// ★ポイント5
				xhr.send(upload_file);
			};

			// ★ポイント6
			document.getElementById("uploadButton").addEventListener(
					'click',
					function() {
						var element_file = document
								.getElementById("uploadFile");
						var upload_file = element_file.files[0];
						
						post_file(upload_file);
					}, false);
		})();
		-->
	</script>
</body>
</html>
```

**★ポイント1**
`File`オブジェクトから各種ファイルの情報を取得します。
今回は①ファイル名、②ファイルサイズ（バイト単位）、③mimeタイプを取得します。
詳細については「 https://developer.mozilla.org/ja/docs/Web/API/File 」を参照ください。

**★ポイント2**
`XMLHttpRequest`オブジェクトを生成し、HTTPヘッダ等の必要な情報を設定します。
はじめにでも説明しましたが`content-type`は`multipart/form-data`ではなく★ポイント1で取得したファイルのものを設定します。

**★ポイント3**
`XMLHttpRequest`の`onload`プロパティにアップロード完了時に実行するコールバック処理を記述します。

**★ポイント4**
今回の記事の本質ではありませんが、折角なのでアップロードの進行状況をプログレスバーで表示するようにしてみました。
`XMLHttpRequest`の`upload.onprogress`プロパティにアップロード中に実行するコールバック処理を記述します。

**★ポイント5**
`XMLHttpRequest`の`send()`メソッドに`File`オブジェクトを設定し、HTTPリクエストを発行します。
詳細については「 https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest#send() 」を参照ください。

**★ポイント6**
「送信」ボタンが押された際にアップロード処理が実行されるように`click`イベントにリスナーを登録します。
`input type="file"`では複数のファイルの選択も可能なため、１つ目の`File`オブジェクトを選択します。
詳細については「 https://developer.mozilla.org/ja/docs/Web/API/FileList 」を参照ください。

## 3. さいごに

今回はajaxでマルチパートを利用しないでファイルをアップロードする方法について説明しました。
**ファイルダウンロードの場合、HTTPレスポンスのBODYはファイルのバイナリデータのみが格納されているため、アップロードでも同じデータ構成でもおかしくないのではないでしょうか。
デメリットとしてはサーバ側もこれに対応した実装が必要となることです。テストでは400MB弱のファイルを6秒でアップロードすることができました。**
ファイルアップロード＝`multipart/form-data`というイメージがありますが、必ずしもそうとは限らないというのを理解して頂けたかと思います。