しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

HTML5 の Drag and Drop API と File API を使ってファイルアップロードを実装する

HTML5 の Drag and Drop API を使うことで、ブラウザにドロップされたファイルの情報を扱うことが出来ます。そして File API を組み合わせることで、ファイルの中身まで扱うことが出来るようになります。

そして FormData オブジェクトと XHR を組み合わせることでアップロード処理までを実装することが出来ます。完成図はこんな感じです。

f:id:shiba-yan:20110702000258p:plain

今回は JavaScript の処理が中心です。jQuery を使っているので追加してくださいね。

$(function () {
    var uploadFiles = function (files) {
        // FormData オブジェクトを用意
        var fd = new FormData();

        // ファイル情報を追加する
        for (var i = 0; i < files.length; i++) {
            fd.append("files", files[i]);
        }

        // XHR で送信
        $.ajax({
            url: "@Url.Action("upload", "file")",
            type: "POST",
            data: fd,
            processData: false,
            contentType: false
        });
    };

    // ファイル選択フォームからの入力
    $("#form").bind("change", function () {
        // 選択されたファイル情報を取得
        var files = this.files;

        // アップロード処理
        uploadFiles(files);
    });

    // ドラッグドロップからの入力
    $("#target").bind("drop", function (e) {
        // ドラッグされたファイル情報を取得
        var files = e.originalEvent.dataTransfer.files;

        // アップロード処理
        uploadFiles(files);
    })
    .bind("dragenter", function () {
        // false を返してデフォルトの処理を実行しないようにする
        return false;
    })
    .bind("dragover", function () {
        // false を返してデフォルトの処理を実行しないようにする
        return false;
    });
});

Firefox 4 などでは FormData オブジェクトが用意されているので、非常に面倒な multipart/form-data の処理を行う必要がありません。IE10 PP2 でも対応したと開発者ガイドに書いてあったのですが、現時点のバージョンでは非対応のようなので使えませんでした。今後に期待します。

それでは HTML 側を見てみましょう。こっちは大したことないです。

<h2>Form Version</h2>
<input id="form" type="file" multiple="multiple" />

<br />

<h2>Drag and Drop Version</h2>
<div id="target" draggable="true">
    <br />
    <br />
    <span>Drop File(s)</span>
    <br />
    <br />
    <br />
</div>

違いは type="file" の input タグに multiple="multiple" を追加しているだけです。IE10 PP2 から使えるようになった、複数のファイルを選択可能にするための属性です。

そしてドラッグドロップのターゲットになっている div タグには draggable="true" を追加しています。この設定をしないとドロップターゲットになりません。

最後にコントローラとアクションですが、引数に追加するだけです。もはや説明もいらないでしょう。

public class FileController : Controller
{
    public ActionResult Upload()
    {
        return View();
    }

    [HttpPost]
    public ActionResult Upload(HttpPostedFileBase[] files)
    {
        // 何かやる!!
        return View();
    }
}

今回はサンプルなので Upload アクションで JSON とか返してないんですけど、本来なら何らかの処理結果を返して JavaScript 側で何とかしてくださいね。

それにしても HTML5 はいろいろ出来て楽しいですね。そういえば File API と書いておきながら File Reader とか使ってなかったですね、まあいいや。