User's collector

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

Опубликована новая книга «Flex Solutions: Essential Techniques for Flex 2 and Flex 3 Developers» Марко Касарио (Marco Casario). Автор делится в ней своими практическими знаниями и техниками, которые он использует при создании Flex 2 и Flex 3 приложений. Книгу сейчас можно предварительно заказать на Amazon.com.

В принципе радует, что авторы и книгоиздатели уже начинают уделять внимание будущей версии Flex фреймворка. Хотя в тоже время огорчает, что на русском мы эту книгу врядли получим, а если и получим, то очень не скоро. Вообще на русском ощущается острый дефицит книг по Flex и ActionScript 3. Начинаю задумываться, может бросить все, сосредоточиться и самому написать такую книгу? Интересно, насколько с экономической точки зрения это будет выгодным мероприятием? =)

Вот еще, подсмотрено в блоге Геннадия «SHAGGYSMILE» Уханова: вышла в продажу новая Библия ActionScript 3.0. За подробностями отправляю к первоисточнику.

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

Теги:

Когда опубликовывал исходники FirebugTarget, я всего на всего хотел поделиться своим опытом отладки flex/as3-приложений. И я действительно был приятно удивлен, когда FirebugTarget пришелся по вкусу не только мне, и поэтому я решил привести код в порядок, снабдить его документацией и выложить в подобающем виде.
Однако в процессе приведения в порядок я понял, что изначально заложил лишнее в FirebugTarget, и что за передачу сообщений консоли Firebug и обработку сообщений логгера должны отвечать разные классы. И вот теперь я готов представить вашему вниманию обновленный FirebugTarget и новый FirebugConsole.

Рассказывать еще раз о FirebugTarget думаю не стоит, так как класс претерпел только внутренние изменения. А вот о FirebugConsole пару слов все-таки скажу. Класс предоставляет прямой доступ к API Firebug-консоли (точнее к его части). То есть, если вам надо просто отправить какое-либо сообщение в консоль, вам достаточно воспользоваться одним из методов FirebugConsole. Кроме этих методов, в классе есть два дополнительных метода для группирования сообщений в консоли, что иногда может быть полезно.

Пожалуй все. Все желающие могут скачать архив с исходниками и документацией. Удачи! :)

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

Теги:

Оригинал статьи: Getting Started With Red5 Server
Автор: Milan Toth
Перевод: Алексей «Vooparker» Аникутин

Часть первая — Установка среды

В целом это статья в большей степени посвящена Java, чем ActionScript, однако если ты flash-flex разработчик, то рано или поздно тебе потребуется столкнуться с разработкой проектов под сервер Red5.
Прежде всего, нам потребуется установить сам сервер Red5 версии 0.6 (для успешного завершения этого урока автор предлагает использовать именно эту версию, так изменения между релизами могут быть весьма значительны, со всеми вытекающими отсюда последствиями. Прим. переводчика — на версии 0.6.1 полет нормальный).

Нам также в этом уроке потребуется Eclipse, если у тебя его еще до сих пор нет, тогда тебе сюда. Устанавливаем и Eclipse и Red5 в «штатном» режиме. Так же было бы не плохо обзавестись XMLBuddy), плагином к Eclipse, он будет нам полезен для редактирования конфигурационных xml-файлов. Устанавливается просто — копированием в папку [Eclipse_HOME]/plugings. Так же там потребуется Adobe Flex Builder 2, автор предлагает установить его в качестве плагина к существующей установке Eclipse, поскольку очень удобно разрабатывать и серверную и клиентскую часть не переключаясь с одной среды разработки на другую.

Если у вас не установлено ни JRE, ни JDK вам придется их установить. Взять их можно здесь. Вам также придется разобраться с java CLASSPATH, за подробностями можете обращаться на страницы Sun.

И так настало время ознакомиться с корневой директорией установки Red5: red5.jar содержит необходимые классы для разработки под red5, в директории webapps располагаются наши приложения. Jetty servlet container ищет приложения в этой директории по конфигурационным xml-файлам, расположенных в папках WEB-INF.

Мы можем создать наше red5 приложение многими способами. Мы можем использовать Apache Ant для компиляции нашего приложения, основанного на red5/build.xml, но этот способ сложнее. Мы воспользуемся Eclipse’ом для написания и компиляции нашего приложения, после чего запустим Red5.

Запускаем Eclipse, кликаем по пункту меню File → New Project, выбираем Java Project, в качестве названия вводим Red5FirstApp и нажимаем Finish.

В виде Package Explorer появился только что созданный проект с библиотекой классов установленной в системе JRE. Кликаем правой кнопкой по проекту, New → Folder, называем директорию WEB-INF. В только что созданной WEB-INF создаем папки classes и src.

Теперь скажем Eclipse, что в качестве корневой директории для наших исходных файлов будет использовать папка WEB-INF/src, для этого правый клик на ней Build Path → Use as Source Folder.

Для хранения скомпилированных классов мы должны установить директорию WEB-INF/classes. Для этого кликаем пункт Project → Properties. Выбираем Java Build Path → вкладка Source. В самом низу находим Default Output Folder, нажимаем Choose и выбираем WEB-INF/classes.

И так мы почти готовы, осталось последнее — расположить конфигурационный файлы. Открываем папку [red5_HOME]/doc/templates/myapp/WEB-INF и копируем все 4 файла в [Eclipse_Workspace]/Red5FirstApp/WEB-INF. После чего переключаемся в Eclipse, правым кликом на проекте и выбираем Refresh, после чего скопированные файлы появятся в проекте.

Часть вторая – Написание простого приложения

И так все готово для того, чтобы приступить к написанию нового приложения. Правый клик на WEB-INF/src, выбираем New → Class, в качестве пакета указываем com.milgra, в качестве имени класса — Application, после чего нажимаем Finish. Созданный нами класс появится в редакторе.

Этот класс и будет основным классом нашего приложения, однако для этого необходимо чтобы он наследовался от класса ApplicationAdapter, входящего в библиотеку red5. Так что для начала нам потребуется добавиться эту библиотеку к проекту. Кликаем пункт Project → Properties, выбираем Java Build Path, затем вкладку Libraries и нажимаем Add External JARs, находим red5.jar директории установки Red5. Теперь Eclipse сможет скомпилировать наше приложение и подсказывать нам.

Сначала создадим два метода appStart и appStop:

JAVA:
  1. package com.milgra;
  2. import org.red5.server.adapter.ApplicationAdapter;
  3. public class Application extends ApplicationAdapter
  4. {
  5.     public boolean appStart ( )
  6.     {
  7.  
  8.     }
  9.     public void appStop ( )
  10.     {
  11.  
  12.     }
  13. }

Было бы не плохо отследить клиентское соединение, для этого нам потребуется импортировать новый класс:

JAVA:
  1. import org.red5.server.api.IConnection;

Теперь мы можем добавить еще два метода:

JAVA:
  1. public boolean appConnect( IConnection conn , Object[] params )
  2. {
  3.     return true;
  4. }
  5.  
  6. public void appDisconnect( IConnection conn , Object[] params )
  7. {
  8.  
  9. }

Для отладки нашего приложения мы воспользуемся логгингом, мы создадим логгер, который будет выводить сообщения в стандартный аутпут. Так что давайте импортируем классы Log и LogFactory:

JAVA:
  1. import org.apache.commons.logging.Log;
  2. import org.apache.commons.logging.LogFactory;

Eclipse укажет на ошибки в этих строках, все потому что мы не импортировали соответствующую библиотеку. Так что давайте сделаем это. Кликаем Project → Properties. Раздел Java Build Path, вкладка Libraries. Нажимаем Add External JARs и выбираем commons-logging-1.1.jar в директории [red5_HOME]/lib и нажимаем OK.

Дополним наш класс:

JAVA:
  1. private static final Log log = LogFactory.getLog( Application.class );
  2.  
  3. public boolean appStart ( )
  4. {
  5.     log.info( "Red5First.appStart" );
  6.     return true;
  7. }
  8.  
  9. public void appStop ( )
  10. {
  11.     log.info( "Red5First.appStop" );
  12. }
  13.  
  14. public boolean appConnect( IConnection conn , Object[] params )
  15. {
  16.     log.info( "Red5First.appConnect " + conn.getClient().getId() );
  17.     return true;
  18. }
  19.  
  20. public void appDisconnect( IConnection conn , Object[] params )
  21. {
  22.     log.info( "Red5First.appDisconnect " + conn.getClient().getId() );
  23. }

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

JAVA:
  1. private static final Log log = LogFactory.getLog( Application.class );
  2.  
  3. public boolean appStart ( )
  4. {
  5.     log.info( "Red5First.appStart" );
  6.     return true;
  7. }
  8.  
  9. public void appStop ( )
  10. {
  11.     log.info( "Red5First.appStop" );
  12. }
  13.  
  14. public boolean appConnect( IConnection conn , Object[] params )
  15. {
  16.     log.info( "Red5First.appConnect " + conn.getClient().getId() );
  17.  
  18.     boolean accept = (Boolean)params[0];
  19.  
  20.     if ( !accept ) rejectClient( "you passed false..." );
  21.  
  22.     return true;
  23. }
  24.  
  25. public void appDisconnect( IConnection conn , Object[] params )
  26. {
  27.     log.info( "Red5First.appDisconnect " + conn.getClient().getId() );
  28. }<

Итоговый код должен выглядеть так:

JAVA:
  1. package com.milgra;
  2.  
  3. import org.apache.commons.logging.Log;
  4. import org.apache.commons.logging.LogFactory;
  5.  
  6. import org.red5.server.api.IConnection;
  7. import org.red5.server.adapter.ApplicationAdapter;
  8.  
  9. public class Application extends ApplicationAdapter
  10. {
  11.     private static final Log log = LogFactory.getLog( Application.class );
  12.  
  13.     public boolean appStart ( )
  14.     {
  15.         log.info( "Red5First.appStart" );
  16.         return true;
  17.     }
  18.  
  19.     public void appStop ( )
  20.     {
  21.         log.info( "Red5First.appStop" );
  22.     }
  23.  
  24.     public boolean appConnect( IConnection conn , Object[] params )
  25.     {
  26.         log.info( "Red5First.appConnect " + conn.getClient().getId() );
  27.  
  28.         boolean accept = (Boolean)params[0];
  29.  
  30.         if ( !accept ) rejectClient( "you passed false..." );
  31.  
  32.         return true;
  33.     }
  34.  
  35.     public void appDisconnect( IConnection conn , Object[] params )
  36.     {
  37.         log.info( "Red5First.appDisconnect " + conn.getClient().getId() );
  38.     }
  39. }

И так мы полностью разобрались с Java. Все что нам осталось это разобраться с конфигурационными файлами. Так что, все наше вниманием теперь четырем файлам в папке WEB-INF.

  • log4j.properties:
    параметры логгинга нашего приложения;
  • red5-web.properties:
    этот файл инклудится red5-web.xml, постоянные параметры могут располагаются здесь. webapp.contextPath хранит в себе путь к нашему приложению (не стоит писать в начале пути webapps), установим значение этого параметра на firstapp:

    CODE:
    1. webapp.contextPath=/firstapp
    2. webapp.virtualHosts=localhost, 127.0.0.1

    теперь файл можно сохранить.

  • red5-web.xml:
    Spring загружает и конфигурирует наше приложение основываясь на этом файле. Bean обработчики также могут располагаться здесь.
    В этом файле находим web.handler и в значение атрибута class вписываем путь до нашего приложения:

    XML:
    1. <bean id="web.handler"
    2.         class="com.milgra.Application"
    3.         singleton="true" />

    Также можете удалить myhandler.service, так в нашем случае нам он не потребуется.

  • web.xml:
    Это файл будет прочитан Jetty первым. Переименуйте приложение здесь в firstapp, и удалите две секции в которых описывается gateway.

    XML:
    1. <context -param>
    2.     <param -name>webAppRootKey</param>
    3.     <param -value>/firstapp</param>
    4. </context>

Ну что ж теперь можно собрать наш проект. Project → Build Automatically, теперь Eclipse будет перекомпиливать проект после каждого изменения, что не может не радовать.

Последний шаг — установка приложения на сервер. Создайте новую директорию в папке red5/webapps и назовите ее firstapp (это имя мы указывали в файле red5-web.properties). И скопируйте в эту директорию WEB-INF из [Eclipse_Workspace]/Red5FirstApp. Теперь запускаем сервер red5 и наше приложение готово.

Часть третья — Создание клиентского flash-приложения

И так серверная часть готова, настало время опробовать ее в работе.

И так переключаемся во Flex Builder, создаем новый Actionscript project и называем его Red5FirstClient:

Actionscript:
  1. package
  2. {
  3.  
  4.     import flash.net.NetConnection;
  5.     import flash.net.ObjectEncoding;
  6.     import flash.events.NetStatusEvent;
  7.     import flash.display.Sprite;
  8.  
  9.     public class Red5FirstClient extends Sprite
  10.     {
  11.  
  12.         private var nc:NetConnection;
  13.  
  14.         public function Red5FirstClient()
  15.         {
  16.  
  17.             // new netconnection
  18.  
  19.             nc = new NetConnection( );
  20.  
  21.             // set encoding to old amf
  22.  
  23.             nc.objectEncoding = ObjectEncoding.AMF0;
  24.  
  25.             // netstatus event listening
  26.  
  27.             nc.addEventListener( NetStatusEvent.NET_STATUS , netStatus );
  28.  
  29.             // connect to red5, passing false as parameter
  30.  
  31.             nc.connect( "rtmp://localhost/firstapp" , false );
  32.  
  33.         }
  34.  
  35.         private function netStatus ( event:NetStatusEvent ):void
  36.         {
  37.  
  38.             trace( event.info.code );
  39.  
  40.             if ( event.info.code == "NetConnection.Connect.Rejected" )
  41.             {
  42.  
  43.                 // trace reject message
  44.  
  45.                 trace( event.info.application );
  46.  
  47.             }
  48.  
  49.         }
  50.  
  51.     }
  52.  
  53. }

Теперь кликаем Run → Debug. Так как в качестве параметра при соединении мы передали false, red5-приложение отклонит попытку, а в логе появиться строка:

[INFO] 87028 pool-2-thread-8:( com.milgra.Application.appConnect ) Red5First.appConnect 1

Что означает, что наше клиентское приложение подключилось к серверу. Теперь проверим flex-консоль:

CODE:
  1. NetConnection.Connect.Rejected
  2. you passed false . . .
  3. NetConnection.Connect.Closed

Серверное приложение отклонило соединение, а также передало сообщение, то есть оно работает так, как мы и планировали.
Теперь изменим значение параметра передаваемого при соединении на true, и посмотрим что произойдет:

Actionscript:
  1. nc.connect( "rtmp://localhost/firstapp" , true );

в консоле мы увидим:

CODE:
  1. NetConnection.Connect.Success

А это означает, что наше приложение работает! :)

UPD: Файлы к статье "Начало разработки с Red5"

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

Теги:

До появления 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 комментария

Теги:

Началась история с того, что я так и не смог привыкнуть к использованию trace() во Flex Builder’e. А после того, как перешел на разработку во FlashDevelop, я вообще перестал пользоваться trace’ом, и если возникала необходимость «показать то, что скрыто» прибегал к помощи Alert.show(). Но сами понимаете, с таким подходом далеко не уйдешь. Поэтому я решил написать свой простенький логгер, но не успел, его уже написали до меня, и при этом наделили весьма неплохим функционалом.

И так, пакет mx.logging.* содержит все, что нужно для организации логгинга в вашем приложении. С содержимым пакета и с тем, как организовать логгинг, можно ознакомиться в ActionScript 3.0 Language and Components Reference и в книге Building and Deploying Flex 2 Applications официальной документации по Flex 2. Скажу только одно, что за обработку сообщений логгера должны отвечать наследники класса mx.logging.AbstractTarget, например, mx.logging.targets.LineFormattedTarget, который предоставляет возможности по форматированию сообщения логгера.

Ну что ж, писать свой логгер мне не пришлось (смогу поспать лишний час – другой =)), мне достаточно будет написать свой таргет. Остается вопрос, куда этот таргет будет вываливать сообщения логгера? И тут я вспомнил про консольку Firebug. Firebug, если вдруг кто не знает, - это расширение для Firefox, предназначенное в первую очередь для отладки JavaScript, а также обладающее и другим не менее полезным функционалом. Проще говоря — это мастхэв.

Дело за написанием своего таргета не стало. Использовать LineFormattedTarget в качестве надкласса я не стал, а решил наследоваться от AbstractTarget. В результате у меня получился FirebugTarget, который, несмотря на то, что наследуется не от LineFormattedTarget, обладает теми же возможностями по форматированию сообщений.

В итоге я получил весьма неплохой, на мой взгляд, способ «подсмотреть то, что скрыто». Если вам это затея показалась интересной, то вы можете посмотреть скриншот и скачать исходники, чтобы посмотреть на FirebugTarget в действии.

Легкой вам отладки!

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

Теги: