[AS3] PHPとAS3の連携 multipart/form-dataでデータのアップロード

2010/10/7

こんにちは、きんくまです。
今回は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>

見た目はこんな感じ

multi_fig1

そんで実際に実行ボタンを押してから、Firebugでどんなデータが送られてるかチェックしてみます。

接続 > すべて > POST > submit.php > POSTのタブ
を順番にクリックしていくと下のような画面になります。

multi_fig2

それで、ずーっとその下もスクロールしていくとこんな記述が出てきます。

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アプリ 好評発売中!
フォルメモ - シンプルなフォルダつきメモ帳
ジッピー電卓 - 消費税や割引もサクサク計算!

ページトップへ戻る