До появления ActionScript 3 единственным вариантом сохранить изображение на сервере была передача значения каждого пикселя объекта BitmapData серверному скрипту. Теперь с появлением объекта ByteArray и с помощью последнего релиза AMFPHP мы можем с легкостью передать объект BitmapData в виде последовательности байтов.
Оригинал статьи: Send and Receive ByteArray to AMFPHP
Автор: Alessandro Crugnola
Перевод: Алексей «Vooparker» Аникутин
Перед прочтением этой статьи автор советует ознакомиться с этими материалами:
1. Экипировка
Чтобы успешно справиться c этим уроком нам потребуются:
2. Настройка аmfphp-проекта
Сначала настроим серверную часть нашего проекта. Для это в папке amfphp/services создайте директорию: “tutorials/amfphp_bytearray“. И в только что созданной директории создайте файл SaveJPEG.php со следующим содержанием:
<?php
class SaveJPEG
{
var $output_dir = "temp";
var $server_url = "http://www.sephiroth.it/amfphp2/services/tutorials/amfphp_bytearray/";
/**
* Save image from the given bytearray
* and return the path of the saved image
*/
function SaveAsJPEG($ba, $compressed = false)
{
if(!file_exists($this->output_dir) || !is_writeable($this->output_dir))
trigger_error ("please create a 'temp' directory first with write access", E_USER_ERROR);
$data = $ba->data;
if($compressed)
{
if(function_exists(gzuncompress))
{
$data = gzuncompress($data);
} else {
trigger_error ("gzuncompress method does not exists, please send uncompressed data", E_USER_ERROR);
}
}
file_put_contents($this->output_dir . "/rawdata.jpeg", $data);
return $this->server_url . $this->output_dir . "/rawdata.jpeg";
}
/**
* Save file from a given bytearray
* and return a ByteArray from the saved file
*/
function SaveAsByteArray($ba, $compresses = false)
{
if(!file_exists($this->output_dir) || !is_writeable($this->output_dir))
trigger_error ("please create a 'temp' directory first with write access", E_USER_ERROR);
$data = $ba->data;
if($compressed)
{
if(function_exists(gzuncompress))
{
$data = gzuncompress($data);
} else {
trigger_error ("gzuncompress method does not exists, please send uncompressed data", E_USER_ERROR);
}
}
file_put_contents($this->output_dir . "/rawdata.rgb", $data);
return new ByteArray(file_get_contents($this->output_dir . "/rawdata.rgb"));
}
}
?>
В той же директории amfphp_bytearray создайте пустую директорию temp c маской доступа 755. Мы будем использовать эту директорию для хранения временных файлов, которые были получены из flash.
В нашем классе всего два метода: SaveAsJPEG и SaveAsByteArray. Оба принимают два параметра:
- экземпляр ByteArray
- и флаг указывающий сжата ли передаваемая последовательность байтов
Так как метод compress класса ByteArray использует алгоритм сжатия zlib, необходимо чтобы в вашей установке PHP была доступна функция gzuncompress.
2.1 SaveAsJPEG
Этот метод получает из flash последовательность байтов, которая представляет собой jpeg файл. По этой причине, мы просто сохраняем файл как ‘rawdata.jpg‘, а так как это изображение будет валидным jpeg файлом, и мы просто скажем flash загрузить его как обычное изображение.
2.2 SaveAsByteArray
В этом случае мы сохраним последовательность байтов так как есть, это означает, что изображение не может быть загружено как обычный файл изображения (вы также не сможете просмотреть его в браузере). Так что, во втором примере мы вернем flash экземпляр php ByteArray, содержащий данные сохраненного файла (используя для этого file_get_content).
Flash получит его как последовательность байтов и, таким образом, мы сможем с легкостью использовать его для создания экземпляра BitmapData.
3. Flex
Давайте посмотрим на наш .mxml файл:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute"
xmlns:display="flash.display.*"
xmlns:ns1="http://www.sephiroth.it/2006/mxml">
<mx:RemoteObject id="service" showBusyCursor="true" destination="amfphp" fault="faultHandler(event)" source="tutorials.amfphp_bytearray.SaveJPEG">
<mx:method name="SaveAsJPEG" result="savejpeg_resultHandler(event)" />
<mx:method name="SaveAsByteArray" result="savebyte_resultHandler(event)" />
</mx:RemoteObject>
<mx:Style>
NumericPopUp {
slider-border-style:solid;
slider-border-color:#999999;
slider-background-color:#EBEBEB;
direction:horizontal;
}
Application {
background-color: #FFFFFF;
}
</mx:Style>
<mx:Script>
<![CDATA[
import mx.core.UIComponent;
import mx.controls.Alert;
import mx.controls.SWFLoader;
import com.adobe.images.JPGEncoder;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
private var send_compressed:Boolean = false; // send always uncompressed bytearrays (i.e. your server doesn not support "gzuncompress")
private var encoder:JPGEncoder;
/**
* Default handler for the remote SaveAsJPEG function
*/
private function savejpeg_resultHandler(event:ResultEvent):void
{
if(event.result || event.result is String)
{
var path:String = event.result as String;
trace(path);
var swf_loader:SWFLoader = preview_box.getChildByName("preview") as SWFLoader;
swf_loader.showBusyCursor = true;
swf_loader.load(path + "?rand=" + new Date().getTime());
image_size.text = "Loading...";
}
}
/**
* Default handler for the remote SaveAsByteArray function
*/
private function savebyte_resultHandler(event:ResultEvent):void
{
var ba:ByteArray = event.result as ByteArray;
var ui_loader:UIComponent = preview_box.getChildByName("preview") as UIComponent;
image_size.text = 'ByteArray size: ' + Math.round(((ba.length/4)/1024)*100)/100 + ' Kb'
try
{
ba.uncompress();
} catch(err:Error)
{
}
var data:BitmapData = new BitmapData(original_image.width, original_image.height, false, 0);
var bmp:Bitmap = new Bitmap(data);
bmp.name = "image";
data.setPixels(data.rect, ba);
ui_loader.addChild(new Bitmap(data));
ui_loader.width = data.width;
ui_loader.height = data.height;
}
/**
* Default fault handler
*/
private function faultHandler(event:FaultEvent):void
{
Alert.show(event.fault.faultString, "Error: " + event.fault.faultCode);
trace(event.fault.message);
}
/**
* Save the image using the JPEGEncoder class
*/
private function saveJpegHandler(event:MouseEvent):void
{
removeImages();
var swf_loader:SWFLoader = new SWFLoader();
swf_loader.autoLoad = true;
swf_loader.name = "preview";
swf_loader.addEventListener(Event.COMPLETE, function(event:Event):void { image_size.text = 'Image size: ' + (SWFLoader(event.target).bytesTotal/1024).toPrecision(3) + ' Kb' });
preview_box.addChildAt(swf_loader, 0);
var bmpdata:BitmapData = Bitmap(original_image.content).bitmapData;
var ba:ByteArray;
encoder = new JPGEncoder(jpeg_quality.value);
ba = encoder.encode( bmpdata );
if(send_compressed)
ba.compress();
service.getOperation("SaveAsJPEG").send(ba, send_compressed);
image_size.text = "Sending..."
}
/**
* Save the image using only ByteArray derived from
* BitmapData.getPixels
*/
private function saveByteHandler(event:MouseEvent):void
{
removeImages();
var ui_loader:UIComponent = new UIComponent();
ui_loader.name = "preview";
preview_box.addChildAt(ui_loader, 0);
var bmpdata:BitmapData = Bitmap(original_image.content).bitmapData;
var arr:ByteArray = Bitmap(original_image.content).bitmapData.getPixels(new Rectangle(0,0,bmpdata.width, bmpdata.height));
if(send_compressed)
arr.compress();
arr.position = 0;
service.getOperation("SaveAsByteArray").send(arr, send_compressed);
image_size.text = "Sending..."
}
private function removeImages():void
{
if(preview_box.getChildByName("preview"))
preview_box.removeChild(preview_box.getChildByName("preview"));
image_size.text = "";
}
]]>
</mx:Script>
<mx:VBox left="5" top="5" right="5" bottom="5">
<ns1:LabelledBox title="Original Image" direction="horizontal" paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20" labelPlacement="left" labelPadding="10" cornerRadius="5" id="original_box">
<mx:Image x="10" y="33" source="@Embed('images/284541843_b77b917989[1].jpg')" id="original_image"/>
<mx:Grid x="269" y="33">
<mx:GridRow width="100%" height="100%">
<mx:GridItem width="100%" height="100%" verticalAlign="middle">
<mx:Label text="Jpeg quality"/>
</mx:GridItem>
<mx:GridItem width="100%" height="100%" verticalAlign="middle">
<ns1:NumericPopUp id="jpeg_quality" minimum="1" maximum="100" stepSize="1" value="50" direction="horizontal">
</ns1:NumericPopUp>
</mx:GridItem>
</mx:GridRow>
<mx:GridRow width="100%" height="100%">
<mx:GridItem width="100%" height="100%">
</mx:GridItem>
<mx:GridItem width="100%" height="100%" verticalAlign="middle">
<mx:Button label="Save as Jpeg" click="saveJpegHandler(event)" width="100%"/>
</mx:GridItem>
</mx:GridRow>
<mx:GridRow width="100%" height="100%">
<mx:GridItem width="100%" height="100%">
</mx:GridItem>
<mx:GridItem width="100%" height="100%" verticalAlign="middle">
<mx:Button label="Save as ByteArray" click="saveByteHandler(event)" width="100%"/>
</mx:GridItem>
</mx:GridRow>
<mx:GridRow width="100%" height="100%">
<mx:GridItem width="100%" height="100%" colSpan="2">
</mx:GridItem>
</mx:GridRow>
</mx:Grid>
</ns1:LabelledBox>
<ns1:LabelledBox id="preview_box" paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20" labelPlacement="left" labelPadding="10" cornerRadius="5" title="Saved Image" clipContent="false" minWidth="300" minHeight="100" direction="horizontal" width="{original_box.width}">
<mx:Grid>
<mx:GridRow width="100%" height="100%">
<mx:GridItem width="100%" height="100%" verticalAlign="middle">
<mx:Label text="" id="image_size" />
</mx:GridItem>
</mx:GridRow>
<mx:GridRow width="100%" height="100%">
</mx:GridRow>
</mx:Grid>
</ns1:LabelledBox>
</mx:VBox>
</mx:Application>
Для отправки данных на сервер у нас есть два метода: saveJpegHandler и saveByteHandler.
3.1 saveJpegHandler
Этот метод получает экземпляр BitmapData из оригинального изображения, и преобразует его в jpeg, используя JPEGEncoder, передавая в конструктор степень оптимизации jpeg изображения.
var bmpdata:BitmapData = Bitmap(original_image.content).bitmapData;
var ba:ByteArray;
encoder = new JPGEncoder(jpeg_quality.value);
ba = encoder.encode( bmpdata );
Сервер вернет строку, содержащую путь к сохраненному изображению, так что все что нам остается, так это загрузить файл используя SWFLoader.
swf_loader.load(path + "?rand=" + new Date().getTime());
Добавление параметра “?rand” позволяет обойти проблему с кэшированием браузером изображения.
3.2 saveByteHandler
Второй метод отправляет последовательность байтов amfphp, и получает ту же последовательность в качестве результата:
// get the original bitmapdata
var bmpdata:BitmapData = Bitmap(original_image.content).bitmapData;
// transform the bitmapdata into a bytearray
var arr:ByteArray = Bitmap(original_image.content).bitmapData.getPixels(new Rectangle(0,0,bmpdata.width, bmpdata.height));
Метод преобразует BitmapData в ByteArray с помощью метода класса BitmapData getPixels и отправляет на сервер.
Метод AMFPHP возвращает последовательность байтов, так что мы можем использовать ее, чтобы создать BitmapData, используя метод setPixel:
// server returns a bytearray
var ba:ByteArray = event.result as ByteArray;
// create a new bitmapdata to store into the resulting bytearray pixels
var data:BitmapData = new BitmapData(original_image.width, original_image.height, false, 0);
var bmp:Bitmap = new Bitmap(data);
// draw the bytearray into just created bitmapdata
data.setPixels(data.rect, ba);
// then add the bitmap to a new UIComponent
ui_loader.addChild(new Bitmap(data));
ui_loader.width = data.width;
ui_loader.height = data.height;
4. Загрузки
Вы также можете скачать исходные файлы к статье.
Известно ли что-нибудь о релизе 1.9 версии? Ведь чувак, который ее разрабатывал ушел из флеша…