2010年11月9日 星期二

flash10 上傳圖片的問題

flash10 上傳圖片的問題

最近開發某個專案時會用到 flash 上傳圖片的功能,
以往 flash 不管是 as2/as3 要進行上傳圖片,
都沒什麼問題,這次居然發生不能上傳的狀況,
其實之前同事也有預到過同樣的情形,
不過一忙就沒 trace 下去,
今天就徹底把問題找出來一次解決。

先描述一下此問題發生的狀況,
當使用者按下 upload 時 js 會透過 ExternalInterface 呼叫
flash function 進行上傳,就會跳出下列訊息:

SecurityError: Error #2176: 特定動作 (像是顯示彈出式視窗的動作) 只能透過使用者互動叫用,例如滑鼠按下或按下按鈕等等。
at flash.net::URLStream/load()
at flash.net::URLLoader/load()
at pages::HomePage/sendPhotoData()
at pages::HomePage/fromJs_upload()
at Function/http://adobe.com/AS3/2006/builtin::apply()
at flash.external::ExternalInterface$/_callIn()
at Function/<anonymous>()

看這訊息,"特定動作"這到底是指什麼,我也沒開 file browse 彈出式視窗,
我只是要上傳 BitmapData....0rz
而且印象中一堆 swfupload/ajax-uploader/YUI Uploader
都用這種方式(js->flash)來上傳檔案。

請教 google 大神後發現這是 flash10 做的安全性改變,
http://www.adobe.com/devnet/flashplayer/articles/fplayer10_uia_requirements.html
其中 POST APIs 有提到以 RFC1867(Form-based File Upload) 的方式上傳,需要使用者主動 click 才有效。

那要怎麼解決呢?總不能都降回 flash9 吧~~

方法一:Base64
將圖片用 base64 編碼再用 POST 的方式送出,
缺點是 size 會變大 1/3 ,
不過目前上傳圖片都不大,這個缺點也就還能接受。
as3:
var jpegEncoder:JPGEncoder = new JPGEncoder(85);
var bmd:BitmapData = new BitmapData(450 , 340);
bmd.draw(mcBox,null,null,null,null,true);
var jpegBytes:ByteArray = jpegEncoder.encode(bmd);
var urlRequest:URLRequest = new URLRequest();
urlRequest.url = "http://localhost/base64Upload.php";
var variables:URLVariables = new URLVariables();
variables.id = Rndm.integer(1,1000);
variables.file = Base64.encode(jpegBytes)
urlRequest.data = variables;
urlRequest.method = URLRequestMethod.POST;
var listloader:DataLoader = new DataLoader(urlRequest, { name:"myPhotoUpload", noCache:true, onComplete: onBase64DataHandler } );
listloader.load();
view raw gistfile1.as hosted with ❤ by GitHub
php:
<?
$id = $_POST['id'];
$file = $_POST['file'];
$target_path="./temp/".$id.".jpg";
$fp = fopen($target_path, 'a');
fwrite($fp, base64_decode($file));
fclose($fp);
echo "success=true&id=".$id;
?>
view raw gistfile1.php hosted with ❤ by GitHub

方法二:amf
沒錯,沒看錯,用 amf 的方式傳輸就沒這個問題,
真不曉得 adobe 在搞什麼。
不過 .Net 上的 amf 我不熟悉這邊就略過,
amfphp/zendAmf 等有時間再另開一篇來說明吧。

[update:20120606]
方法三:
避開發生問題的 request header "Content-Type" of "multipart/form-data"
as3:
var jpgencoder:JPGEncoder = new JPGEncoder(80);
var ba:ByteArray = jpgencoder.encode(Bitmap(_arrPhotos[0].photo).bitmapData);
var header:URLRequestHeader = new URLRequestHeader("Content-type", "application/octet-stream");
var request:URLRequest = new URLRequest("http://api.wwwins.com.tw/saveImg.php?category=" + arrSwitchBtnValue[indexSwitch]);
var loader:URLLoader = new URLLoader();
request.requestHeaders.push(header);
request.method = URLRequestMethod.POST;
request.data = ba;
loader.load(request);
view raw imageUpload.as hosted with ❤ by GitHub
php:
<?
$postdata = file_get_contents("php://input");
if (isset($postdata)&&strlen($postdata)>0) {
$handle = fopen("upload/file-name.png", "w+");
fwrite($handle, $postdata);
fclose($handle);
echo "r=success&category=".$_GET['category'];
}
else echo "r=error";
?>
view raw saveImg.php hosted with ❤ by GitHub

缺點:
一次只能傳一張照片,
如果要帶參數,只能用 GET 的方式帶在 url 後面,有點小麻煩。

沒有留言:

張貼留言