こんにちは、きんくまです。
今回はmultipart/form-dataでのアップロードです。
multipart/form-dataって何かっていうと、htmlのformで画像ファイルとかのバイナリファイルをアップロードする場合の形式です。
ためしに、今回AS3で作るのとおんなじhtmlフォームのサンプルはこんな感じになります。
<html> <head> </head> <body> <form method="post" action="submit.php" enctype="multipart/form-data"> <p>username: <input name="username" value="" /></p> <p>image: <input name="myimage" type="file" /></p> <p><input type="submit" /></p> </form> </body> </html>
見た目はこんな感じ
そんで実際に実行ボタンを押してから、Firebugでどんなデータが送られてるかチェックしてみます。
接続 > すべて > POST > submit.php > POSTのタブ
を順番にクリックしていくと下のような画面になります。
それで、ずーっとその下もスクロールしていくとこんな記述が出てきます。
Content-Type: multipart/form-data; boundary=---------------------------296432690914902 Content-Length: 63160 -----------------------------296432690914902 Content-Disposition: form-data; name="username" Peter -----------------------------296432690914902 Content-Disposition: form-data; name="myimage"; filename="sample.jpg" Content-Type: image/jpeg ...バイナリとおもわれる文字列がつづく(略) -----------------------------296432690914902--
ときどき ” —————————–296432690914902 ” という記述が続くことがわかります。
これがバウンダリというもので、-(ハイフン)を2つと任意の文字列(そのあとに絶対に同じものが出てこない)で
構成されるみたいです。
>> フォームデータの送信
>> フォームよるファイルアップロードの仕様
それじゃ、これと全く同じものをAS3で作ってあげれば、サーバー側もhtmlから送られたものと同じく処理できちゃうわけです。
そんで、調べてみたら@psyark先生が2年前にすでに作られてました。
>> psyark/MultipartFormDataBuilder – Spark project
おー、すげーと、見つけたときはうなっちゃいましたよ。「なるほど!こうやるのかー」っていう感じ。
だったんですが、ファイルアップの部分がなぜかうまく動かなかったので、ちょっとだけ改造したものが下のやつです。PNGでアップしたい人は、JPEGの部分を適宜おきかえれば大丈夫だと思います。
2012/09/04 —————
コメントで指摘があったので修正
1) multipart/form-data; boundary=sampleboundary 区切りはセミコロン
2) 最後のバウンダリを記述するときだけ、–(ハイフン2つ)がバウンダリの後ろに必要だそうです。
–sampleboundary– みたいに。
この部分のコードの方は修正してないので、うまく動かない場合は修正してみてください。
参考
>> http://en.wikipedia.org/wiki/MIME#Multipart_messages
>> multipart/form-data
—————————–
■MultipartFormDataBuilder.as
package jp.psyark.net { import flash.net.URLRequest; import flash.net.URLRequestMethod; import flash.utils.ByteArray; /** * multipart/form-dataのリクエストを作成するための簡単なクラスです。 */ public class MultipartFormDataBuilder { protected var _boundary:String; protected var byteArray:ByteArray; /** * コンストラクタです。 * * @param boundary バウンダリ(境界線)文字列です。送信する他のデータ中に出現しない文字列である必要があります。 */ public function MultipartFormDataBuilder(boundary:String) { _boundary = boundary; byteArray = new ByteArray(); addBoundary(); } /** * パート(部分)を追加します。 * * @param name このパートの名前です。content-dispositionヘッダのname属性に使われます。 * @param data このパートのデータです。ByteArray以外の値は文字列として評価されます。 * @param filename このパートのファイル名です。null以外の値を渡した場合、content-dispositionヘッダのfilename属性に使われます。 */ public function addPart(name:String, data:*, filename:String=null):void { byteArray.writeUTFBytes('Content-Disposition: form-data; name="' + name + '"'); if (filename != null) { byteArray.writeUTFBytes('; filename="' + filename + '"'); byteArray.writeUTFBytes("\r\n"); byteArray.writeUTFBytes("Content-Type: image/jpeg"); //JPEG用 } byteArray.writeUTFBytes("\r\n\r\n"); if (data is ByteArray) { byteArray.writeBytes(data); } else { byteArray.writeUTFBytes(data); } byteArray.writeUTFBytes("\r\n"); addBoundary(); } /** * multipart/form-dataのリクエストとして使えるように、URLRequestを設定します。 */ public function configure(request:URLRequest):void { request.method = URLRequestMethod.POST; request.data = byteArray; request.contentType = "multipart/form-data; boundary=" + _boundary; } private function addBoundary():void { byteArray.writeUTFBytes("--" + _boundary + "\r\n"); } } }
変更したのはaddPartのif(filename のあたり。
それで、これを使うサンプルクラスはこんな感じです。
これを使うのに、Jpegエンコードする必要があったのでas3corelibを使いました。
■Main.as
package { import com.adobe.images.JPGEncoder; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.MouseEvent; import flash.net.URLLoader; import flash.net.URLRequest; import flash.utils.ByteArray; import jp.psyark.net.MultipartFormDataBuilder; /** * ... * @author KinkumaDesign */ public class Main extends Sprite { public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); //何かのユーザーインタラクションをしないとエラーになる。だからダミーでステージクリック stage.addEventListener(MouseEvent.CLICK, clickHD); } private function clickHD(e:MouseEvent):void { stage.removeEventListener(MouseEvent.CLICK, clickHD); var bmd:BitmapData = new BitmapData(100, 100); //ダミーのBitmapData bmd.fillRect(bmd.rect, 0xffff0000); //JPEG エンコード var encoder:JPGEncoder = new JPGEncoder(100); //画質はとりあえず100 var byteArr:ByteArray = encoder.encode(bmd); var builder:MultipartFormDataBuilder = new MultipartFormDataBuilder("247672365515574"); //バウンダリは適当 builder.addPart("username", "Peter"); //テキスト builder.addPart("myimage", byteArr, "dummy.jpg"); //JPEGエンコードされたByteArray var req:URLRequest = new URLRequest("submit.php"); builder.configure(req); var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, loaderCompleteHD); loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHD); loader.load(req); } private function ioErrorHD(e:IOErrorEvent):void { throw new Error(e.text); } private function loaderCompleteHD(e:Event):void { trace("comp"); } } }
これでOKです。受ける側のPHPはhtmlだろうが、AS3だろうが共通で使えます。
■submit.php
<?php echo $_POST['username']."\n"; echo $_FILES['myimage']['name']."\n"; echo $_FILES['myimage']['type']."\n"; echo $_FILES['myimage']['size']."\n"; echo $_FILES['myimage']['tmp_name']."\n"; echo $_FILES['myimage']['error']."\n"; $path = $_FILES['myimage']['tmp_name']; //ファイルをtmpフォルダから指定の場所へコピー $filename = $_FILES['myimage']['name']; move_uploaded_file($path, $filename); ?>
$_FILEでずーっとエラーになってたんだけど、$_FILESってSをつけたらOKになりました。
>> http://www.php-ref.com/web/03_move_uploaded_file.html
>> CGI PHP でファイルアップロード
PHPとAS3の連携のまとめ
ていうわけで、文字列とか画像ファイルの受け渡しも含めてAS3とPHPの連携をやってみました。
わかっちゃえば意外と簡単なんだけど、わかるまでが大変でした、、。
以下が各ページです。
>> [AS3] PHPとAS3の連携 ByteArrayでサーバー側から受け取ったJPEG画像を読み込む
>> [AS3] PHPとAS3の連携 POSTでテキストを取得
>> [AS3] PHPとAS3の連携 GETでXML取得
>> [AS3] PHPとAS3の連携 GETでテキスト取得
■ 自作iPhoneアプリ 好評発売中!
・フォルメモ - シンプルなフォルダつきメモ帳
・ジッピー電卓 - 消費税や割引もサクサク計算!
■ LINEスタンプ作りました!
毎日使える。とぼけたウサギ