LoginSignup
20

More than 5 years have passed since last update.

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

Posted at

1. はじめに

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

(ポイント)

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

2. ソースコード

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-typemultipart/form-dataではなく★ポイント1で取得したファイルのものを設定します。

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

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

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

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

3. さいごに

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

ファイルアップロード=multipart/form-dataというイメージがありますが、必ずしもそうとは限らないというのを理解して頂けたかと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20