Ajax的に画像などのファイルをアップロードする方法
Posted: Updated:
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メソッドライクなインターフェースですね。