Реализация интерфейса IFilePromise

Adobe AIR 2 и более поздних версий

Чтобы предоставить предварительные образы файла для ресурсов, доступ к которым с помощью объекта URLFilePromise невозможен, можно реализовать интерфейс IFilePromise в пользовательском классе. Интерфейс IFilePromise определяет методы и свойства, используемые средой выполнения AIR для доступа к данным, которые необходимо записать в файл после перетаскивания предварительного образа файла.

Реализация IFilePromise передает другой объект среде выполнения AIR, которая предоставляет данные для предварительного образа файла. Этот объект должен реализовать интерфейс IDataInput, который применяется средой выполнения AIR для чтения данных. Например, класс URLFilePromise, реализующий IFilePromise, использует в качестве поставщика данных объект URLStream.

AIR может синхронно или асинхронно считывать данные. Реализация IFilePromise сообщает о поддерживаемом режиме доступа путем возврата соответствующего значения в свойство isAsync . Если предоставляется асинхронный доступ к данным, объект поставщика данных должен реализовать интерфейс IEventDispatcher и отправить необходимые события (такие как open , progress и complete ).

Можно воспользоваться пользовательским классом, либо одним из следующих встроенных классов в качестве поставщика данных для предварительного образа файла:

  • ByteArray (синхронный)

  • FileStream (синхронный или асинхронный)

  • Socket (асинхронный)

  • URLStream (асинхронный)

Чтобы реализовать интерфейс IFilePromise, необходимо предоставить код для следующих функций и свойств.

  • open():IDataInput — возвращает объект поставщика данных, из которого считываются данные для файла. Объект должен реализовывать интерфейс IDataInput. Если данные предоставляются асинхронно, объект должен также реализовывать интерфейс IEventDispatcher и отправлять необходимые события (см. раздел Использование асинхронного поставщика данных в предварительном образе файла ).

  • get relativePath():String — указывает путь создаваемого файла, включая имя файла. Этот путь разрешается относительно местоположения перетаскивания, выбранного пользователем во время операции перетаскивания. Чтобы убедиться в том, что в пути используется правильный символ разделителя для операционной системы хоста, воспользуйтесь константой File.separator при указании путей с каталогами. Чтобы дать возможность задавать путь во время выполнения, можно добавить функцию установщика или воспользоваться параметром конструктора.

  • get isAsync():Boolean — сообщает среде выполнения AIR о синхронном или асинхронном предоставлении данных объектом поставщика данных.

  • close():void — вызывается средой выполнения, когда данные полностью считаны (или при возникновении ошибки, предотвращающей дальнейшее чтение). Эту функцию можно применять для очистки ресурсов.

  • reportError( e:ErrorEvent ):void — вызывается средой выполнения при появлении ошибки чтения данных.

Все методы IFilePromise вызываются средой выполнения во время операции перетаскивания, включающей предварительный образ файла. Как правило, ваша логика приложения не должна вызывать какие-либо из этих методов напрямую.

Использование синхронного поставщика данных в предварительном образе файла

Самый простой способ реализации интерфейса IFilePromise состоит в том, чтобы использовать объект синхронного поставщика данных (например, ByteArray или синхронный FileStream). В следующем примере создается заполненный данными объект ByteArray, который возвращается при вызове метода open() .

package 
{ 
    import flash.desktop.IFilePromise; 
    import flash.events.ErrorEvent; 
    import flash.utils.ByteArray; 
    import flash.utils.IDataInput; 
     
    public class SynchronousFilePromise implements IFilePromise 
    { 
        private const fileSize:int = 5000; //size of file data 
        private var filePath:String = "SynchronousFile.txt"; 
         
        public function get relativePath():String 
        { 
            return filePath; 
        } 
         
        public function get isAsync():Boolean 
        { 
            return false; 
        } 
         
        public function open():IDataInput 
        { 
            var fileContents:ByteArray = new ByteArray(); 
             
            //Create some arbitrary data for the file 
            for( var i:int = 0; i < fileSize; i++ ) 
            { 
                fileContents.writeUTFBytes( 'S' ); 
            } 
             
            //Important: the ByteArray is read from the current position             
            fileContents.position = 0; 
            return fileContents; 
        } 
         
        public function close():void 
        { 
            //Nothing needs to be closed in this case. 
        } 
         
        public function reportError(e:ErrorEvent):void 
        { 
            trace("Something went wrong: " + e.errorID + " - " + e.type + ", " + e.text ); 
        } 
    } 
}

На практике синхронные предварительные образы файла имеют ограниченное применение. Если данных мало, можно с легкостью создать файл во временном каталоге и добавить обычный массив со списком файлов в буфер обмена при перетаскивании. С другой стороны, если объем данных велик или генерирование данных требует больших вычислительных ресурсов, требуется продолжительный синхронный процесс. Продолжительные синхронные процессы могут блокировать обновления пользовательского интерфейса в течение заметного периода времени и создавать впечатление «зависания» программы. Во избежание появления этой проблемы можно создать асинхронный поставщик данных, который включается таймером.

Использование асинхронного поставщика данных в предварительном образе файла

При использовании объекта асинхронного поставщика данных свойству isAsync интерфейса IFilePromise должно быть присвоено значение true , а объект, возвращенный методом open() , должен реализовать интерфейс IEventDispatcher. Среда выполнения прослушивает несколько альтернативных событий, чтобы в качестве поставщика данных можно было использовать разные встроенные объекты. Например, события progress отправляются объектами FileStream и URLStream, а события socketData отправляются объектами Socket. Среда выполнения прослушивает соответствующие события из всех этих объектов.

Процесс считывания данных из объекта поставщика данных зависит от следующих событий.

  • Event.OPEN — сообщает среде выполнения о готовности источника данных.

  • ProgressEvent.PROGRESS — сообщает среде выполнения о доступности данных. Среда выполнения считает объем доступных данных из объекта поставщика данных.

  • ProgressEvent.SOCKET_DATA — сообщает среде выполнения о доступности данных. Событие socketData отправляется объектами на базе сокета. Для других типов объектов необходимо отправить событие progress . Среда выполнения прослушивает оба события, чтобы определить, когда можно прочесть данные.

  • Event.COMPLETE — сообщает среде выполнения о том, что все данные считаны.

  • Event.CLOSE — сообщает среде выполнения о том, что все данные считаны. Для этой цели среда выполнения прослушивает как событие close , так и событие complete .

  • IOErrorEvent.IOERROR — сообщает среде выполнения о появлении ошибки чтения данных. Среда выполнения останавливает создание файла и вызывает метод IFilePromise close() .

  • SecurityErrorEvent.SECURITY_ERROR — сообщает среде выполнения о появлении ошибки безопасности. Среда выполнения останавливает создание файла и вызывает метод IFilePromise close() .

  • HTTPStatusEvent.HTTP_STATUS — используется средой выполнения вместе с httpResponseStatus , чтобы убедиться в том, что доступные данные отражают нужное содержимое, а не сообщение об ошибке (такое как «страница 404»). Это событие должны отправлять объекты, основанные на протоколе HTTP.

  • HTTPStatusEvent.HTTP_RESPONSE_STATUS — используется средой выполнения вместе с httpStatus , чтобы убедиться в том, что доступные данные отражают нужное содержимое. Это событие должны отправлять объекты, основанные на протоколе HTTP.

Поставщик данных должен отправлять эти события в следующей последовательности:

  1. событие open

  2. события progress или socketData ,

  3. событие complete или close .

Примечание. Встроенные объекты (FileStream, Socket и URLStream) автоматически отправляют соответствующие события.

В следующем примере предварительный образ файла создается с помощью пользовательского асинхронного поставщика данных. Класс поставщика данных является расширением ByteArray (для поддержки IDataInput) и реализует интерфейс IEventDispatcher. При каждом событии таймера объект генерирует порцию данных и отправляет событие progress, чтобы сообщить среде выполнения о доступности данных. После получения достаточного объема данных объект отправляет событие complete.

package 
{ 
import flash.events.Event; 
import flash.events.EventDispatcher; 
import flash.events.IEventDispatcher; 
import flash.events.ProgressEvent; 
import flash.events.TimerEvent; 
import flash.utils.ByteArray; 
import flash.utils.Timer; 
 
[Event(name="open", type="flash.events.Event.OPEN")] 
[Event(name="complete",  type="flash.events.Event.COMPLETE")] 
[Event(name="progress", type="flash.events.ProgressEvent")] 
[Event(name="ioError", type="flash.events.IOErrorEvent")] 
[Event(name="securityError", type="flash.events.SecurityErrorEvent")] 
public class AsyncDataProvider extends ByteArray implements IEventDispatcher 
{ 
    private var dispatcher:EventDispatcher = new EventDispatcher(); 
    public var fileSize:int = 0; //The number of characters in the file 
    private const chunkSize:int = 1000; //Amount of data written per event 
    private var dispatchDataTimer:Timer = new Timer( 100 ); 
    private var opened:Boolean = false; 
     
    public function AsyncDataProvider() 
    { 
        super(); 
        dispatchDataTimer.addEventListener( TimerEvent.TIMER, generateData ); 
    } 
 
    public function begin():void{ 
        dispatchDataTimer.start(); 
    } 
     
    public function end():void 
    { 
        dispatchDataTimer.stop(); 
    } 
    private function generateData( event:Event ):void 
    { 
        if( !opened ) 
        { 
            var open:Event = new Event( Event.OPEN ); 
            dispatchEvent( open );     
            opened = true; 
        } 
        else if( position + chunkSize < fileSize ) 
        { 
            for( var i:int = 0; i <= chunkSize; i++ ) 
            { 
                writeUTFBytes( 'A' ); 
            } 
            //Set position back to the start of the new data 
            this.position -= chunkSize; 
            var progress:ProgressEvent = 
                new ProgressEvent( ProgressEvent.PROGRESS, false, false, bytesAvailable, bytesAvailable + chunkSize); 
            dispatchEvent( progress ) 
        } 
        else 
        { 
            var complete:Event = new Event( Event.COMPLETE ); 
            dispatchEvent( complete );         
        } 
    } 
     //IEventDispatcher implementation 
    public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void 
    { 
        dispatcher.addEventListener( type, listener, useCapture, priority, useWeakReference ); 
    } 
     
    public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void 
    { 
        dispatcher.removeEventListener( type, listener, useCapture ); 
    } 
     
    public function dispatchEvent(event:Event):Boolean 
    { 
        return dispatcher.dispatchEvent( event ); 
    } 
     
    public function hasEventListener(type:String):Boolean 
    { 
        return dispatcher.hasEventListener( type ); 
    } 
     
    public function willTrigger(type:String):Boolean 
    { 
        return dispatcher.willTrigger( type ); 
    } 
} 
}
Примечание. Поскольку класс AsyncDataProvider в данном примере является расширением ByteArray, он не может также являться расширением EventDispatcher. Чтобы реализовать интерфейс IEventDispatcher, класс использует внутренний объект EventDispatcher и перенаправляет вызовы метода IEventDispatcher в этот внутренний объект. Можно также расширить EventDispatcher и реализовать IDataInput (или реализовать оба интерфейса).

Асинхронная реализация интерфейса IFilePromise почти идентична синхронной реализации. Основные различия состоят в том, что isAsync возвращает true и что метод open() возвращает объект асинхронных данных:

package 
{ 
    import flash.desktop.IFilePromise; 
    import flash.events.ErrorEvent; 
    import flash.events.EventDispatcher; 
    import flash.utils.IDataInput; 
     
    public class AsynchronousFilePromise extends EventDispatcher implements IFilePromise 
    { 
        private var fileGenerator:AsyncDataProvider; 
        private const fileSize:int = 5000; //size of file data 
        private var filePath:String = "AsynchronousFile.txt"; 
                 
        public function get relativePath():String 
        { 
            return filePath; 
        } 
         
        public function get isAsync():Boolean 
        { 
            return true; 
        } 
         
        public function open():IDataInput 
        { 
            fileGenerator = new AsyncDataProvider(); 
            fileGenerator.fileSize = fileSize; 
            fileGenerator.begin(); 
            return fileGenerator; 
        } 
         
        public function close():void 
        { 
            fileGenerator.end(); 
        } 
         
        public function reportError(e:ErrorEvent):void 
        { 
            trace("Something went wrong: " + e.errorID + " - " + e.type + ", " + e.text ); 
        } 
    } 
}