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メソッドライクなインターフェースですね。