User's collector

Внимание!   Данная опция будет доступна только после того, как вы авторизуетесь.
   запомнить меня 

Те из вас, кто собирает свои as3 приложения с помощью mxmlc знакомы с метатэгом [Embed], который используется для внедрения внешних ресурсов в конечный swf. Среди поддерживаемых mxmlc типов внедряемых ресурсов присутствуют файлы изображения (JPG, JPEG, PNG и SVG), swf файлы, символы из swf файлов, mp3 файлы, файлы системных и TrueType шрифтов. Но не все знают, что с помощью [Embed] можно внедрить любой файл в виде байтовой последовательности.
Как вы знаете включение ресурсов возможно как на уровне свойства так и на уровне класса. Это справедливо и для файлов, которые мы включаем в виде байтовой последовательности. В примере ниже мы включаем в наш клип текстовый файл на уровне свойства (обратите внимание на дополнительный параметр mimeType, именно он сообщает mxmlc, что мы хотим включить файл как байтовую последовательность):

Actionscript:
  1. [Embed(source="readme.txt", mimeType="application/octet-stream")]
  2. public var Readme:Class;

Если вы хотите включить свой файл на уровне класса то вы должны будете наследоваться от mx.core.ByteArrayAsset:

Actionscript:
  1. package
  2. {
  3.     import mx.core.ByteArrayAsset;
  4.    
  5.     [Embed(source="readme.txt", mimeType="application/octet-stream")]
  6.     public class Readme extends ByteArrayAsset {}
  7. }

После этого, чтобы создать экземепляр класса нашего включенного файла просто вызываем конструктор:

Actionscript:
  1. var readme:ByteArrayAsset = new Readme(); // для уровня свойства
  2. var readme:Readme = new Readme(); // для уровня класса

Так как в любом случае наш ресурс является наследником или экземпляром ByteArrayAsset, который в свою очередь расширяет ByteArray, то мы можем использовать все методы последнего. Например, прочитать содержимое текстового файла из нашего примера можно так:

Actionscript:
  1. var readmeContent:String = readme.readUTFBytes(readme.length);

В бинарном виде вы можете включать и файлы поддерживаемых типов. Например, вы можете включить другой swf в виде последовательности байтов, после чего можно внести swf в список видимых объектов или получить доступ к его внутренним ресурсам, предварительно использовав Loader:

Actionscript:
  1. package
  2. {
  3.     import flash.display.Loader;
  4.     import flash.display.Sprite;
  5.     import flash.events.Event;
  6.    
  7.     public class EmbedResourceExample extends Sprite
  8.     {
  9.         [Embed(source="assets/resources.swf", mimeType="application/octet-stream")]
  10.         private var Resources:Class;
  11.        
  12.         public function EmbedResourceExample ()
  13.         {
  14.             var loader:Loader = new Loader();
  15.             // Подписываемся на событие INIT, после которого
  16.             // мы получим доступ к классам внутри включенного swf
  17.             loader.contentLoaderInfo.addEventListener(Event.INIT, resourceInitHandler);
  18.             // Загружаем swf как байтовую последовательность
  19.             loader.loadBytes(new Resources());
  20.         }
  21.        
  22.         private function resourceInitHandler (e:Event):void
  23.         {
  24.             // Извлекаем и создаем экземпляр класса из включенного swf
  25.             var SomeClass:Class = e.target.content.loaderInfo.applicationDomain.getDefinition("SomeClass") as Class;
  26.             var someClass:Object = new SomeClass();
  27.         }
  28.     }
  29. }

Пожалуй на этом все.
Напоследок хочу отметить, что метатэг [Embed] не распознается flash compiler и включить тот же текстовый файл в swf силами Flash IDE не получится.
Удачи!

7 комментариев

Теги:

До появления ActionScript 3 единственным вариантом сохранить изображение на сервере была передача значения каждого пикселя объекта BitmapData серверному скрипту. Теперь с появлением объекта ByteArray и с помощью последнего релиза AMFPHP мы можем с легкостью передать объект BitmapData в виде последовательности байтов.
Оригинал статьи: Send and Receive ByteArray to AMFPHP
Автор: Alessandro Crugnola
Перевод: Алексей «Vooparker» Аникутин

Перед прочтением этой статьи автор советует ознакомиться с этими материалами:

  1. Export JPEG with Flash/PHP.
  2. Flex RemoteObject and AMFPHP 1.9 (ru).

1. Экипировка

Чтобы успешно справиться c этим уроком нам потребуются:

2. Настройка аmfphp-проекта

Сначала настроим серверную часть нашего проекта. Для это в папке amfphp/services создайте директорию: "tutorials/amfphp_bytearray". И в только что созданной директории создайте файл SaveJPEG.php со следующим содержанием:

PHP:
  1. <?php
  2. class SaveJPEG
  3. {
  4.     var $output_dir = "temp";
  5.     var $server_url = "http://www.sephiroth.it/amfphp2/services/tutorials/amfphp_bytearray/";
  6.  
  7.     /**
  8.      * Save image from the given bytearray
  9.      * and return the path of the saved image
  10.      */
  11.     function SaveAsJPEG($ba, $compressed = false)
  12.     {
  13.         if(!file_exists($this->output_dir) || !is_writeable($this->output_dir))
  14.             trigger_error ("please create a 'temp' directory first with write access", E_USER_ERROR);
  15.  
  16.         $data = $ba->data;
  17.         if($compressed)
  18.         {
  19.             if(function_exists(gzuncompress))
  20.             {
  21.                 $data = gzuncompress($data);
  22.             } else {
  23.                 trigger_error ("gzuncompress method does not exists, please send uncompressed data", E_USER_ERROR);
  24.             }
  25.         }
  26.         file_put_contents($this->output_dir . "/rawdata.jpeg", $data);
  27.         return $this->server_url . $this->output_dir . "/rawdata.jpeg";
  28.     }
  29.  
  30.     /**
  31.      * Save file from a given bytearray
  32.      * and return a ByteArray from the saved file
  33.      */
  34.     function SaveAsByteArray($ba, $compresses = false)
  35.     {
  36.         if(!file_exists($this->output_dir) || !is_writeable($this->output_dir))
  37.             trigger_error ("please create a 'temp' directory first with write access", E_USER_ERROR);
  38.  
  39.         $data = $ba->data;
  40.         if($compressed)
  41.         {
  42.             if(function_exists(gzuncompress))
  43.             {
  44.                 $data = gzuncompress($data);
  45.             } else {
  46.                 trigger_error ("gzuncompress method does not exists, please send uncompressed data", E_USER_ERROR);
  47.             }
  48.         }
  49.         file_put_contents($this->output_dir . "/rawdata.rgb", $data);
  50.         return new ByteArray(file_get_contents($this->output_dir . "/rawdata.rgb"));
  51.     }
  52. }
  53. ?>

В той же директории 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:
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mx:Application
  3.     xmlns:mx="http://www.adobe.com/2006/mxml"
  4.     layout="absolute"
  5.     xmlns:display="flash.display.*"
  6.     xmlns:ns1="http://www.sephiroth.it/2006/mxml">
  7.     <mx:RemoteObject id="service" showBusyCursor="true" destination="amfphp" fault="faultHandler(event)" source="tutorials.amfphp_bytearray.SaveJPEG">
  8.         <mx:method name="SaveAsJPEG" result="savejpeg_resultHandler(event)" />
  9.         <mx:method name="SaveAsByteArray" result="savebyte_resultHandler(event)" />
  10.     </mx:RemoteObject>
  11.     <mx:Style>
  12.         NumericPopUp {
  13.             slider-border-style:solid;
  14.             slider-border-color:#999999;
  15.             slider-background-color:#EBEBEB;
  16.             direction:horizontal;
  17.         }
  18.  
  19.         Application {
  20.             background-color: #FFFFFF;
  21.         }
  22.     </mx:Style>
  23.     <mx:Script>
  24.         <![CDATA[
  25.             import mx.core.UIComponent;
  26.             import mx.controls.Alert;
  27.             import mx.controls.SWFLoader;
  28.             import com.adobe.images.JPGEncoder;
  29.             import mx.rpc.events.FaultEvent;
  30.             import mx.rpc.events.ResultEvent;
  31.  
  32.             private var send_compressed:Boolean = false;    // send always uncompressed bytearrays (i.e. your server doesn not support "gzuncompress")
  33.             private var encoder:JPGEncoder;
  34.  
  35.             /**
  36.              * Default handler for the remote SaveAsJPEG function
  37.              */
  38.             private function savejpeg_resultHandler(event:ResultEvent):void
  39.             {
  40.                 if(event.result || event.result is String)
  41.                 {
  42.                     var path:String = event.result as String;
  43.                     trace(path);
  44.  
  45.                     var swf_loader:SWFLoader = preview_box.getChildByName("preview") as SWFLoader;
  46.                     swf_loader.showBusyCursor = true;
  47.                     swf_loader.load(path + "?rand=" + new Date().getTime());
  48.  
  49.                     image_size.text = "Loading...";
  50.                 }
  51.             }
  52.  
  53.             /**
  54.              * Default handler for the remote SaveAsByteArray function
  55.              */
  56.             private function savebyte_resultHandler(event:ResultEvent):void
  57.             {
  58.                 var ba:ByteArray = event.result as ByteArray;
  59.                 var ui_loader:UIComponent = preview_box.getChildByName("preview") as UIComponent;
  60.                 image_size.text = 'ByteArray size: ' + Math.round(((ba.length/4)/1024)*100)/100 + ' Kb'
  61.  
  62.                 try
  63.                 {
  64.                     ba.uncompress();
  65.                 } catch(err:Error)
  66.                 {
  67.                 }
  68.  
  69.                 var data:BitmapData = new BitmapData(original_image.width, original_image.height, false, 0);
  70.                 var bmp:Bitmap = new Bitmap(data);
  71.                 bmp.name = "image";
  72.                 data.setPixels(data.rect, ba);
  73.  
  74.                 ui_loader.addChild(new Bitmap(data));
  75.                 ui_loader.width = data.width;
  76.                 ui_loader.height = data.height;
  77.  
  78.             }
  79.  
  80.             /**
  81.              * Default fault handler
  82.              */
  83.             private function faultHandler(event:FaultEvent):void
  84.             {
  85.                 Alert.show(event.fault.faultString, "Error: " + event.fault.faultCode);
  86.                 trace(event.fault.message);
  87.             }
  88.  
  89.             /**
  90.              * Save the image using the JPEGEncoder class
  91.              */
  92.             private function saveJpegHandler(event:MouseEvent):void
  93.             {
  94.                 removeImages();
  95.  
  96.                 var swf_loader:SWFLoader = new SWFLoader();
  97.                 swf_loader.autoLoad = true;
  98.                 swf_loader.name = "preview";
  99.                 swf_loader.addEventListener(Event.COMPLETE, function(event:Event):void { image_size.text = 'Image size: ' + (SWFLoader(event.target).bytesTotal/1024).toPrecision(3) + ' Kb' });
  100.                 preview_box.addChildAt(swf_loader, 0);
  101.  
  102.                 var bmpdata:BitmapData = Bitmap(original_image.content).bitmapData;
  103.                 var ba:ByteArray;
  104.  
  105.                 encoder = new JPGEncoder(jpeg_quality.value);
  106.                 ba = encoder.encode( bmpdata );
  107.  
  108.                 if(send_compressed)
  109.                     ba.compress();
  110.  
  111.                 service.getOperation("SaveAsJPEG").send(ba, send_compressed);
  112.  
  113.                 image_size.text = "Sending..."
  114.             }
  115.  
  116.             /**
  117.              * Save the image using only ByteArray derived from
  118.              * BitmapData.getPixels
  119.              */
  120.             private function saveByteHandler(event:MouseEvent):void
  121.             {
  122.                 removeImages();
  123.  
  124.                 var ui_loader:UIComponent = new UIComponent();
  125.                 ui_loader.name = "preview";
  126.                 preview_box.addChildAt(ui_loader, 0);
  127.  
  128.                 var bmpdata:BitmapData = Bitmap(original_image.content).bitmapData;
  129.                 var arr:ByteArray = Bitmap(original_image.content).bitmapData.getPixels(new Rectangle(0,0,bmpdata.width, bmpdata.height));
  130.  
  131.                 if(send_compressed)
  132.                     arr.compress();
  133.  
  134.                 arr.position = 0;
  135.                 service.getOperation("SaveAsByteArray").send(arr, send_compressed);
  136.  
  137.                 image_size.text = "Sending..."
  138.             }
  139.  
  140.             private function removeImages():void
  141.             {
  142.                 if(preview_box.getChildByName("preview"))
  143.                     preview_box.removeChild(preview_box.getChildByName("preview"));
  144.  
  145.                 image_size.text = "";
  146.             }
  147.  
  148.         ]]>
  149.     </mx:Script>
  150.     <mx:VBox left="5" top="5" right="5" bottom="5">
  151.         <ns1:LabelledBox title="Original Image" direction="horizontal" paddingBottom="20" paddingLeft="20" paddingRight="20" paddingTop="20" labelPlacement="left" labelPadding="10" cornerRadius="5" id="original_box">
  152.             <mx:Image x="10" y="33" source="@Embed('images/284541843_b77b917989[1].jpg')"  id="original_image"/>
  153.             <mx:Grid x="269" y="33">
  154.                 <mx:GridRow width="100%" height="100%">
  155.                     <mx:GridItem width="100%" height="100%" verticalAlign="middle">
  156.                         <mx:Label text="Jpeg quality"/>
  157.                     </mx:GridItem>
  158.                     <mx:GridItem width="100%" height="100%" verticalAlign="middle">
  159.                         <ns1:NumericPopUp id="jpeg_quality" minimum="1" maximum="100" stepSize="1" value="50" direction="horizontal">
  160.                         </ns1:NumericPopUp>
  161.                     </mx:GridItem>
  162.                 </mx:GridRow>
  163.                 <mx:GridRow width="100%" height="100%">
  164.                     <mx:GridItem width="100%" height="100%">
  165.                     </mx:GridItem>
  166.                     <mx:GridItem width="100%" height="100%" verticalAlign="middle">
  167.                         <mx:Button label="Save as Jpeg" click="saveJpegHandler(event)" width="100%"/>
  168.                     </mx:GridItem>
  169.                 </mx:GridRow>
  170.                 <mx:GridRow width="100%" height="100%">
  171.                     <mx:GridItem width="100%" height="100%">
  172.                     </mx:GridItem>
  173.                     <mx:GridItem width="100%" height="100%" verticalAlign="middle">
  174.                         <mx:Button label="Save as ByteArray" click="saveByteHandler(event)" width="100%"/>
  175.                     </mx:GridItem>
  176.                 </mx:GridRow>
  177.                 <mx:GridRow width="100%" height="100%">
  178.                     <mx:GridItem width="100%" height="100%" colSpan="2">
  179.                     </mx:GridItem>
  180.                 </mx:GridRow>
  181.             </mx:Grid>
  182.         </ns1:LabelledBox>
  183.         <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}">
  184.             <mx:Grid>
  185.                 <mx:GridRow width="100%" height="100%">
  186.                     <mx:GridItem width="100%" height="100%" verticalAlign="middle">
  187.                         <mx:Label text="" id="image_size" />
  188.                     </mx:GridItem>
  189.                 </mx:GridRow>
  190.                 <mx:GridRow width="100%" height="100%">
  191.                 </mx:GridRow>
  192.             </mx:Grid>
  193.         </ns1:LabelledBox>
  194.     </mx:VBox>
  195. </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. Загрузки

Вы также можете скачать исходные файлы к статье.

3 комментария

Теги: