Implementowanie interfejsu IFilePromise

Adobe AIR 2 i starsze wersje

Aby udostępnić obietnice plików reprezentujące zasoby, do których nie można uzyskać dostępu przez obiekt URLFilePromise, można zaimplementować interfejs IFilePromise w klasie niestandardowej. Interfejs IFilePromise definiuje metody i właściwości używane przez środowisko AIR do uzyskiwania dostępu do danych, które mają być zapisane w pliku po upuszczeniu obietnicy.

Implementacja IFilePromise przekazuje do środowiska AIR następny obiekt, który udostępnia dane dla obietnicy pliku. Obiekt ten musi implementować interfejs IDataInput, którego środowisko AIR używa do odczytywania danych. Na przykład klasa URLFilePromise, która implementuje interfejs IFilePromise, używa obiektu URLStream jako dostawcy danych.

Środowisko AIR może odczytywać dane synchronicznie lub asynchronicznie. Implementacja IFilePromise informuje, który tryb dostępu jest obsługiwany, zwracając odpowiednią wartość we właściwości isAsync . Jeśli obsługiwany jest asynchroniczny dostęp do danych, dostawca danych musi implementować interfejs IEventDispatcher i wywoływać niezbędne zdarzenia, takie jak open , progress i complete .

W charakterze dostawcy danych dla obietnicy pliku można użyć własnej klasy niestandardowej lub jednej z klas wbudowanych:

  • ByteArray (dostęp synchroniczny)

  • FileStream (dostęp synchroniczny i asynchroniczny)

  • Socket (dostęp asynchroniczny)

  • URLStream (dostęp asynchroniczny)

Aby zaimplementować interfejs IFilePromise, należy udostępnić kod następujących funkcji i właściwości:

  • open():IDataInput — zwraca obiekt dostawy danych, z którego odczytywane są dane obiecanego pliku. Obiekt musi implementować interfejs IDataInput. Jeśli dane są udostępniane asynchronicznie, obiekt musi także implementować interfejs IEventDispatcher i wywoływać odpowiednie zdarzenia (zobacz Korzystanie z asynchronicznego dostawcy danych w obietnicy plików ).

  • get relativePath():String — udostępnia ścieżkę utworzonego pliku wraz z jego nazwą. Ścieżka jest interpretowana względem miejsca upuszczenia wybranego przez użytkownika w operacji przeciągania i upuszczania. Aby mieć pewność, że w ścieżce użyty będzie separator właściwy dla systemu operacyjnego hosta, należy w ścieżkach z katalogami używać stałej File.separator . Można dodać funkcję ustawiającą lub użyć parametru konstruktora, aby umożliwić ustawienie ścieżki w trakcie wykonywania.

  • get isAsync():Boolean — informuje środowisko wykonawcze AIR, czy obiekt dostawcy danych udostępnia dane synchronicznie czy asynchronicznie.

  • close():void — wywoływana przez środowisko wykonawcze, gdy dane zostaną w całości odczytane (lub błąd uniemożliwi dalszy odczyt). Można użyć tej funkcji do usuwania zbędnych zasobów.

  • reportError( e:ErrorEvent ):void — wywoływana przez środowisko wykonawcze, gdy wystąpi błąd podczas odczytu danych.

Wszystkie metody interfejsu IFilePromise są wywoływane przez środowisko wykonawcze podczas operacji przeciągania i upuszczania obietnicy plików. Zwykle logika aplikacji nie powinna bezpośrednio wywoływać żadnej z tych metod.

Korzystanie z synchronicznego dostawcy danych w obietnicy plików

Najprostszym sposobem na zaimplementowanie interfejsu IFilePromise jest użycie obiektu synchronicznego dostawcy danych, takiego jak ByteArray lub synchroniczny obiekt FileStream. W poniższym przykładzie obiekt ByteArray jest tworzony, wypełniany danymi i zwracany po wywołaniu metody 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 ); 
        } 
    } 
}

W praktyce synchroniczne obietnice plików mają ograniczone zastosowanie. Jeśli ilość danych jest niewielka, równie dobrze można utworzyć plik w katalogu tymczasowym i dodać tablicę z listą normalnych plików do schowka przeciągania i upuszczania. Jeśli zaś dane mają dużą objętość lub ich generowanie jest kosztowne obliczeniowo, proces synchroniczny będzie trwał długo. Długotrwały proces synchroniczny może zauważalnie wstrzymywać aktualizacje interfejsu użytkownika i sprawić, że aplikacja nie będzie odpowiednio reagować na działania użytkownika. Aby uniknąć tego problemu, można utworzyć asynchronicznego dostawcę danych sterowanego przez licznik czasu.

Korzystanie z asynchronicznego dostawcy danych w obietnicy plików

W przypadku użycia obiektu asynchronicznego dostawcy danych właściwość isAsync obiektu IFilePromise musi być równa true , a obiekt zwrócony przez metodę open() musi implementować interfejs IEventDispatcher. Środowisko wykonawcze wykrywa kilka alternatywnych zdarzeń, tak że w charakterze dostawców danych można użyć różnych obiektów wbudowanych. Na przykład zdarzenia progress są wywoływane przez obiekty FileStream i URLStream, natomiast zdarzenia socketData są wywoływane przez obiekty Socket. Środowisko wykonawcze wykrywa odpowiednie zdarzenia ze wszystkich tych obiektów.

Następujące zdarzenia sterują procesem odczytywania danych z obiektu dostawcy danych:

  • Event.OPEN — informuje środowisko wykonawcze, że źródło danych jest gotowe.

  • ProgressEvent.PROGRESS — informuje środowisko wykonawcze o dostępności danych. Środowisko wykonawcze odczyta dostępną ilość danych z obiektu dostawcy danych.

  • ProgressEvent.SOCKET_DATA — informuje środowisko wykonawcze o dostępności danych. Zdarzenie socketData jest wywoływane przez obiekty działające w oparciu o gniazda. Obiekty innego typu powinny wywoływać zdarzenia progress . (Środowisko wykonawcze wykrywa oba typy zdarzeń informujących o możliwości odczytu danych).

  • Event.COMPLETE — informuje środowisko wykonawcze, że wszystkie dane zostały odczytane.

  • Event.CLOSE — informuje środowisko wykonawcze, że wszystkie dane zostały odczytane. (Środowisko wykonawcze wykrywa zarówno zdarzenia close , jak i complete ).

  • IOErrorEvent.IOERROR — informuje środowisko wykonawcze, że wystąpił błąd podczas odczytu danych. Środowisko wykonawcze przerywa tworzenie pliku i wywołuje metodę IFilePromise close() .

  • SecurityErrorEvent.SECURITY_ERROR — informuje środowisko wykonawcze, że wystąpił błąd zabezpieczeń. Środowisko wykonawcze przerywa tworzenie pliku i wywołuje metodę IFilePromise close() .

  • HTTPStatusEvent.HTTP_STATUS — używane przez środowisko wykonawcze razem z wartością httpResponseStatus w celu potwierdzenia, że dostępne dane są pożądaną treścią, a nie komunikatem o błędzie (takim jak strona błędu nr 404). To zdarzenie powinny wywoływać obiekty oparte na protokole HTTP.

  • HTTPStatusEvent.HTTP_RESPONSE_STATUS — używane przez środowisko wykonawcze razem z wartością httpStatus w celu potwierdzenia, że dostępne dane reprezentują pożądaną treść. To zdarzenie powinny wywoływać obiekty oparte na protokole HTTP.

Dostawca danych powinien wywoływać te zdarzenia w następującej kolejności:

  1. zdarzenie open

  2. zdarzenia progress lub socketData ;

  3. zdarzenie complete lub close .

Uwaga: Wbudowane obiekty FileStream, Socket i URLStream automatycznie wywołują odpowiednie zdarzenia.

Poniższy przykład ilustruje tworzenie obietnicy pliku korzystającej z niestandardowego synchronicznego dostawcy danych. Klasa dostawcy danych rozszerza klasę ByteArray (w celu zaimplementowania interfejsu IDataInput) oraz implementuje interfejs IEventDispatcher. Przy każdym zdarzeniu licznika czasu obiekt generuje fragment danych i wywołuje zdarzenie progress, aby poinformować środowisko wykonawcze o dostępności danych. Po wygenerowaniu dostatecznej ilości danych obiekt wywołuje zdarzenie 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 ); 
    } 
} 
}
Uwaga: Ponieważ klasa AsyncDataProvider w tym przykładzie rozszerza klasę ByteArray, nie może jednocześnie rozszerzać klasy EventDispatcher. Do zaimplementowania interfejsu IEventDispatcher wykorzystano wewnętrzny obiekt EventDispatcher, a wywołania interfejsu IEventDispatcher są przekazywane do tego wewnętrznego obiektu. Można również rozszerzyć klasę EventDispatcher i zaimplementować interfejs IDataInput (lub zaimplementować oba interfejsy).

Asynchroniczna implementacja interfejsu IFilePromise niemal nie różni się od implementacji synchronicznej. Główna różnica polega na tym, że właściwość isAsync zwraca wartość true , a metoda open() zwraca asynchroniczny obiekt danych:

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 ); 
        } 
    } 
}