Ajax的に画像などのファイルをアップロードする方法

JavaScriptでファイルアップロード?



a-blog cmsの開発中、Ajax的にファイルをアップロードする必要があったので、その際の調べ物についてかきます。普通にpostしようにも、JavaScriptでローカルのファイルは扱えないよ、って話で、先輩から「iframeがヒントだ!」的なざっくりとしたアドバイスを頂いたので、それを頼りに調べる。

おお、なるほどなるほど。以下、HTML, JavaScript, PHPのサンプルをメモ。

こんなHTMLをJavaScriptで操作すればいい

JavaScript自身が要素の生成を作成してもよいですが、最終的にはこういうHTMLが作成されることになります。

<a href="#" id="uploadTrigger">アップロードするぜー</a>

<form id="uploadForm" action="画像POST先" method="post" enctype="multipart/form-data" target="ajaxPostImage">
    <input type="file" name="imageFile" />
</form>

<iframe name="ajaxPostImage"></iframe>

実際に、Ajaxっぽい表現をする上では、iframe要素やform要素はdisplay:hiddenなどをして、非表示のままバックグラウンドで動作させます。

target属性で、form要素の送信結果の受け取り先を、iframe[name="ajaxPostImage"]に指定しています。こうすれば、form要素をsubmitしても、画面遷移を発生させずにリクエストを送信できます。

実際にJavaScriptで操作するとこうなる

例によって横着jQueryです。イベントのbind周りが適当ですがサンプルなのでご愛敬していただけると。

iframeのloadイベントがキモ

var $form   = $('#uploadForm'),
    $trigger= $('#uploadTrigger'),
    $iframe = $('iframe[name="ajaxPostImage"]');

$trigger.click(function()
{
    $form.submit();
    return false;
});

$form.submit(function()
{
    // submitされた時点で、loadイベントをbind
    $iframe.unbind().bind('load', function() {
        var response = $iframe.contents();

        // responseを調べて、送信後の処理を完結させる
        console.log(response);
    });

});

iframe内に取り込まれてくるレスポンスは、原則的にボディしか取得できないと思うので、ステータスコードに頼らなくても良いように、JSONとかで処理結果を返すと良いです。

サーバー側は適当に受け取ればOK

<?php
die(json_encode($_FILES));
?>

JavaScriptで解析可能なレスポンスを返しつつ、あとはいつも通りで、POSTされたファイルの保存等の処理を行えばOK。

{"imageFile":{
     "name":"uploadfile.png",
     "type":"image\/png",
     "tmp_name":"\/tmp\/phpQ7OqcF",
     "error":0,
     "size":24883
}}

PHPで横着してJSONを扱うときは、PEPr :: Details :: Services_JSONが便利です。自由にできないレンタルサーバーなどの環境だと、json_decode, json_encode等が使えないことも多いので、重宝しています。

余談と参考URL

実装するときは、この記事の内容を気にとめておくと良いです。実装したいUIによっては、このような工夫も必要になります。

上記のような、Safariではiframeにstyle="display:none;"すると良くない、という話があったので確認しましたが、Mac版のSafari5では問題なく動作していました。Safariのバージョンがもうちょっと前の頃の話でしょうかね。

さっくりとファイルアップロードを実装するなら、上記のAjaxFileUploadとかいうプラグインが簡単そうです。jQueryのajaxメソッドライクなインターフェースですね。