Implementieren der IFilePromise-Schnittstelle

Adobe AIR 2 und höher

Zum Bereitstellen von Dateizusagen für Ressourcen, die sich nicht über ein URLFilePromise-Objekt aufrufen lassen, können Sie die IFilePromise-Schnittstelle in einer benutzerdefinierten Klasse implementieren. Die IFilePromise-Schnittstelle definiert die Methoden und Eigenschaften, die die AIR-Laufzeit für den Zugriff auf die Daten verwendet, die in eine Datei geschrieben werden sollen, wenn die Dateizusage abgelegt wird.

Eine IFilePromise-Implementierung übergibt ein anderes Objekt an die AIR-Laufzeit, das die Daten für die Dateizusage bereitstellt. Dieses Objekt muss die IDataInput-Schnittstelle implementieren, die von der AIR-Laufzeit zum Lesen der Daten verwendet wird. Beispielsweise verwendet die URLFilePromise-Klasse, die IFilePromise implementiert, ein URLStream-Objekt als Datenprovider.

AIR kann die Daten synchron oder asynchron lesen. Die IFilePromise-Implementierung meldet den unterstützten Zugriffsmodus durch Rückgabe des entsprechenden Wertes in der isAsync -Eigenschaft. Für den asynchronen Datenzugriff muss das Datenprovider-Objekt die IEventDispatcher-Schnittstelle implementieren und die erforderlichen Ereignisse auslösen, wie open , progress und complete .

Als Datenprovider für eine Dateizusage können Sie eine benutzerdefinierte Klasse oder eine der folgenden integrierten Klassen verwenden:

  • ByteArray (synchron)

  • FileStream (synchron oder asynchron)

  • Socket (asynchron)

  • URLStream (asynchron)

Zum Implementieren der IFilePromise-Schnittstelle müssen Sie Code für die folgenden Funktionen und Eigenschaften angeben:

  • open():IDataInput – Gibt das Datenprovider-Objekt zurück, aus dem die Daten für die zugesagte Datei gelesen werden. Das Objekt muss die IDataInput-Schnittstelle implementieren. Wenn die Daten asynchron bereitgestellt werden, muss das Objekt auch die IEventDispatcher-Schnittstelle implementieren und die erforderlichen Ereignisse auslösen (siehe Verwenden eines asynchronen Datenprovider in einer Dateizusage ).

  • get relativePath():String – Gibt den Pfad, einschließlich des Namens, für die erstellte Datei an. Dieser Pfad wird relativ zu dem Ablageort aufgelöst, den der Anwender beim Ziehen und Ablegen ausgewählt hat. Um sicherzustellen, dass der Pfad das passende Trennzeichen für das Host-Betriebssystem verwendet, geben Sie die File.separator -Konstante an, wenn Sie Pfade festlegen, die Verzeichnisse enthalten. Sie können eine Set-Funktion hinzufügen oder einen Konstruktorparameter verwenden, damit der Pfad zur Laufzeit festgelegt werden kann.

  • get isAsync():Boolean – Informiert die AIR-Laufzeit, ob das Datenprovider-Objekt die Daten synchron oder asynchron bereitstellt.

  • close():void – Wird von der Laufzeit aufgerufen, wenn die Daten vollständig gelesen wurden (oder wenn ein Fehler verhindert, dass die Daten weiter gelesen werden können). Diese Funktion kann zur Ressourcenbereinigung verwendet werden.

  • reportError( e:ErrorEvent ):void – Wird von der Laufzeit aufgerufen, wenn beim Lesen der Daten ein Fehler auftritt.

Alle IFilePromise-Methoden werden während einer Drag & Drop-Operation, die die Dateizusage umfasst, von der Laufzeit aufgerufen. Normalerweise sollte die Anwendungslogik keine dieser Methoden direkt aufrufen.

Verwenden eines synchronen Datenprovider in einer Dateizusage

Die einfachste Methode zur Implementierung der IFilePromise-Schnittstelle ist die Verwendung eines synchronen Datenprovider-Objekts, wie ein ByteArray-Objekt oder ein synchrones FileStream-Objekt. Im folgenden Beispiel wird ein ByteArray-Objekt erstellt, mit Daten gefüllt und dann beim Aufruf der open() -Methode zurückgegeben.

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

In der Praxis sind synchrone Dateizusagen nur von eingeschränktem Nutzen. Bei einer kleinen Datenmenge könnten Sie einfach eine Datei in einem temporären Verzeichnis erstellen und der Drag & Drop-Zwischenablage ein reguläres Dateilisten-Array hinzufügen. Wenn die Datenmenge jedoch groß ist oder wenn die Computerressourcen durch das Generieren der Daten stark beansprucht werden, ist ein langer synchroner Prozess erforderlich. Lange synchrone Prozesse können die Aktualisierung der Benutzeroberfläche deutlich verzögern, sodass der Eindruck entsteht, dass die Anwendung nicht reagiert. Um dieses Problem zu vermeiden, können Sie einen asynchronen Datenprovider erstellen, der von einem Timer gesteuert wird.

Verwenden eines asynchronen Datenprovider in einer Dateizusage

Bei Verwendung eines asynchronen Datenprovider-Objekts muss die isAsync -Eigenschaft von IFilePromise auf true eingestellt sein und das Objekt, das von der open() -Methode zurückgegeben wird, muss die IEventDispatcher-Schnittstelle implementieren. Die Laufzeit wartet auf mehrere alternative Ereignisse, sodass verschiedene integrierte Objekte als Datenprovider verwendet werden können. Beispielsweise werden progress -Ereignisse von FileStream- und URLStream-Objekten ausgelöst, während socketData -Ereignisse von Socket-Objekten ausgelöst werden. Die Laufzeit wartet auf die entsprechenden Ereignisse von allen diesen Objekten.

Die folgenden Ereignisse steuern den Prozess zum Lesen der Daten aus dem Datenprovider-Objekt:

  • Event.OPEN – Informiert die Laufzeit, dass die Datenquelle bereit ist.

  • ProgressEvent.PROGRESS – Informiert die Laufzeit, dass Daten verfügbar sind. Die Laufzeit liest die verfügbare Datenmenge aus dem Datenprovider-Objekt.

  • ProgressEvent.SOCKET_DATA – Informiert die Laufzeit, dass Daten verfügbar sind. Das socketData -Ereignis wird von socketbasierten Objekten ausgelöst. Für andere Objekttypen sollte ein progress -Ereignis ausgelöst werden. (Die Laufzeit wartet auf beide Ereignisse, um festzustellen, wann Daten gelesen werden können.)

  • Event.COMPLETE – Informiert die Laufzeit, dass alle Daten gelesen wurden.

  • Event.CLOSE – Informiert die Laufzeit, dass alle Daten gelesen wurden. (Zu diesem Zweck wartet die Laufzeit auf close und auf complete .)

  • IOErrorEvent.IOERROR – Informiert die Laufzeit, dass beim Lesen der Daten ein Fehler aufgetreten ist. Die Laufzeit bricht die Erstellung der Datei ab und ruft die close() -Methode von IFilePromise auf.

  • SecurityErrorEvent.SECURITY_ERROR – Informiert die Laufzeit, dass ein Sicherheitsfehler aufgetreten ist. Die Laufzeit bricht die Erstellung der Datei ab und ruft die close() -Methode von IFilePromise auf.

  • HTTPStatusEvent.HTTP_STATUS – Wird zusammen mit httpResponseStatus von der Laufzeit verwendet, um sicherzustellen, dass die verfügbaren Daten den gewünschten Inhalt darstellen und keine Fehlermeldung (wie beispielsweise einen 404-Seitenfehler). Objekte auf Basis des HTTP-Protokolls sollten dieses Ereignis ausgeben.

  • HTTPStatusEvent.HTTP_RESPONSE_STATUS – Wird zusammen mit httpStatus von der Laufzeit verwendet, um sicherzustellen, dass die verfügbaren Daten den gewünschten Inhalt darstellen. Objekte auf Basis des HTTP-Protokolls sollten dieses Ereignis ausgeben.

Der Datenprovider sollte diese Ereignisse in der folgenden Reihenfolge ausgeben:

  1. open-Ereignis

  2. progress - oder socketData -Ereignisse

  3. complete - oder close -Ereignisse

Hinweis: Die integrierten Objekte, FileStream, Socket und URLStream, setzen die entsprechenden Ereignisse automatisch ab.

Im folgenden Beispiel wird eine Dateizusage mit einem benutzerdefinierten asynchronen Datenprovider erstellt. Die Datenprovider-Klasse erweitert ByteArray (für die IDataInput-Unterstützung) und implementiert die IEventDispatcher-Schnittstelle. Bei jedem Timer-Ereignis generiert das Objekt einen Datenblock und setzt ein progress-Ereignis ab, um die Laufzeit über die Verfügbarkeit der Daten zu informieren. Wenn ausreichend Daten generiert wurden, setzt das Objekt ein complete-Ereignis ab.

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 ); 
    } 
} 
}
Hinweis: Da die AsyncDataProvider-Klasse im Beispiel ByteArray erweitert, kann sie nicht außerdem EventDispatcher erweitern. Zum Implementieren der IEventDispatcher-Schnittstelle verwendet die Klasse ein internes EventDispatcher-Objekt und leitet die IEventDispatcher-Methodenaufrufe an dieses interne Objekt weiter. Sie könnten auch EventDispatcher erweitern und IDataInput implementieren (oder Sie können beide Schnittstellen implementieren).

Die asynchrone IFilePromise-Implementierung ist mit der synchronen Implementierung fast identisch. Es bestehen die folgenden Hauptunterschiede: isAsync gibt true zurück und die open() -Methode gibt ein asynchrones Datenobjekt zurück:

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