uzullaの日記

本家:about等:Twitter
 | 

2008-06-11

FlexにJPEG画像を読み込ませる為に、一度サーバーで反射させるやり方

| 05:35

最近なんにも書いてないので。

Flexで画像を編集するツールを書いているが、Flexだと当然ローカルファイルがよめないので、一度サーバーにULして、それをDLしてローディングする必要がある。


リフレクター

サーバーPHPとかでこんなのを設置する


<?php
/*
Filedataというnameでファイルをポストすると、
./images/{GUID}_filename.jpg
に保存し、URLの文字列をレスポンスする。
*/

$MAXIMUM_FILESIZE = 1024 * 2000; // 2000KB
$MAXIMUM_FILE_COUNT = 10; // keep maximum 10 files on server

require("Guid.php");
$Guid = new Guid();
$filename = $Guid->toString() . "_" .$_FILES['Filedata']['name'];

if ($_FILES['Filedata']['size'] <= $MAXIMUM_FILESIZE)
{
  move_uploaded_file($_FILES['Filedata']['tmp_name'], "./temporary/".$filename);
  $type = exif_imagetype("./temporary/".$filename);

  if ($type == 1 || $type == 2 || $type == 3){ // GIF or JPG or PNG.
    rename("./temporary/".$filename, "./images/".$filename);
  } else{ // Not popular image format.
    unlink("./temporary/".$filename);
    echo "NG";
    exit;
  }
}else{
  echo "OVERSIZE";
  exit;
}

$directory = opendir('./images/');
$files = array();

while ($file = readdir($directory))
{
  array_push($files, array('./images/'.$file, filectime('./images/'.$file)));
}

usort($files, sorter);

if (count($files) > $MAXIMUM_FILE_COUNT)
{
  $files_to_delete = array_splice($files, 0, count($files) - $MAXIMUM_FILE_COUNT);
  for ($i = 0; $i < count($files_to_delete); $i++)
    {
      @unlink($files_to_delete[$i][0]);
    }
}

closedir($directory);
print("http://localhost/images/".$filename);

function sorter($a, $b)
{
  if ($a[1] == $b[1])
    {
      return 0;
    }
  else
    {
      return ($a[1] < $b[1]) ? -1 : 1;
    }
}
?>

上のコードはかなり適当、Adobeのサンプルコード*1を多少弄った物。

guid.php*2は適当なランダムファイル名を付ける為の物。

処理としては、POSTされたファイルをサーバーに保存し、そのファイルをHTTPでGETできるURLを返す。

途中でファイルが画像であるかチェックしているが、この辺り省けば何のファイルでも使える。


ライブラリ

次はメインとなるライブラリ

package jp.cfe.file
{
	import flash.events.DataEvent;
	import flash.events.Event;
	import flash.net.FileFilter;
	import flash.net.FileReference;
	import flash.net.URLRequest;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	
	import mx.managers.CursorManager;

/**
 * サーバーにファイルをアップロードして、アップロードされたファイルのURLを取得する。
 * var rl:ReflectLoad = new ReflectLoad(callbackFunction);
 * rl.setFileFilter("Images", "*.jpg;*.gif;*.png");
 * rl.browse();
 * 
 * アップロードが終わったらコールバック関数が呼ばれる。
 * 
 */
	public class ReflectLoad
	{
		
		private var _fr:FileReference;
		private var _ff:FileFilter = null;
		
		public var fileUrl:String;
		public var status:Boolean = false;
	
		public var reflectorUrl:String = "http://localhost/ul.php";
		public var uploadParamName:String = "Filedata";

		public var responseHandlerFunction:Function;

		public function ReflectLoad(_func:Function)
		{
			_fr = new FileReference();
			_fr.addEventListener(Event.SELECT, imgSelectHandler);
			_fr.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA,imgUploadCompleteHandler);
			responseHandlerFunction = _func;
		}

		public function browse():void{
			_fr.browse([_ff]);
		}

		public function setFileFilter(desc:String, suffix:String):void{
			_ff = new FileFilter(desc, suffix);
		}

		private function imgSelectHandler(event:Event):void{
			//マウスカーソルをBusyに
			CursorManager.setBusyCursor();
			
		    var request:URLRequest = new URLRequest(reflectorUrl);
		    var params:URLVariables = new URLVariables();

		    request.method = URLRequestMethod.POST;
		    request.data = params;

		    _fr.upload(request,uploadParamName);
		}
		
		private function imgUploadCompleteHandler(event:DataEvent):void{
			 if(event.data.toString().substr(0,4) == 'http'){
		 		//Alert.show(event.data.toString());
				fileUrl = event.data.toString();
				status = true;
				responseHandlerFunction(this);
			 }else{
			 	// maybe, server was failed.
				status = false;
				responseHandlerFunction(this);
			 	//Alert.show("something wrong");
			 }
			 
 			//マウスカーソルをnomalに
			CursorManager.removeBusyCursor();
		}


	}
  
}


利用例

で、上のライブラリはこんな風に使う。

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import mx.controls.Image;
import mx.managers.CursorManager;
	
import jp.cfe.file.ReflectLoad;
	
private _loader:Loader;

//
// reflectLoadを用いて画像をロード 1
// ファイル選択画面を出す
//
private function loadNewImg():void{
	var _rl:ReflectLoad = new ReflectLoad(rlCallback);
	_rl.setFileFilter("Images", "*.jpg;*.gif;*.png");
	_rl.browse();
}
//
// reflectLoadを用いて画像をロード 2
// ULした画像をLoaderでDLロードする
//
private function rlCallback(_rl:ReflectLoad):void{
	if(_rl.status){
		//マウスカーソルをBusyに
		CursorManager.setBusyCursor();
		_loader = new Loader();
		_loader.load( new URLRequest(_rl.fileUrl) );
		_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, completedata );
	}else{
		//ULにミスっているのでエラー表示とか

	}
}
//
// reflectLoadを用いて画像をロード 3
// ロードが終わったloaderのデータをBitmapDataに展開し、諸々の処理をする。
//
public function completedata(e:Event):void{
	bmd = new BitmapData(_loader.content.width, _loader.content.height);
	var _image:Image = new Image();
	_image.source = new Bitmap(bmd);
	this.addChild(_image);
	CursorManager.removeBusyCursor();
}

こんな感じでロードする。


こんな面倒くさい事を本当はしたくないので

セキュリティ的な問題で直接ロードが出来ないようになっていると思うのだが、やる側がやったらこんな風にできるような事なので、fileReferenceから直接ByteArrayとかにロードできる仕組みが欲しいとおもった。

AIRとかだとできるんだけどなー。


後、Flexはイベント駆動なので、単にULしてDLしてロードするのに三個も関数をまたがねばならない。

実際、上の例だと_imageがロードされるイベントも待たなければならないので…めんどくさい。

もっとすっきり書ければいいのにな。

 |