こんにちは、きんくまです。
今回は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の部分を適宜おきかえれば大丈夫だと思います。
■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でテキスト取得
[...] りしなくて済みますね!素敵! きんくまさんのところ。 [AS3] PHPとAS3の連携 as3から直接 multipart/form-data でデータのアップロード! Flashからアップロードする方法いろいろはこちら。バイ [...]