Implementazione dell'interfaccia IFilePromise

Adobe AIR 2 e versioni successive

Per fornire promesse di file per risorse a cui non è possibile accedere utilizzando un oggetto URLFilePromise, potete implementare l'interfaccia IFilePromise in una classe personalizzata. L'interfaccia IFilePromise definisce i metodi e le proprietà utilizzati dal runtime AIR per accedere ai dati da scrivere in un file dopo che la promessa di file è stata rilasciata.

Un'implementazione IFilePromise passa un altro oggetto al runtime AIR che fornisce i dati per la promessa di file. Questo oggetto implementa l'interfaccia IDataInput, utilizzata dal runtime AIR per leggere i dati. Ad esempio, la classe URLFilePromise, che implementa IFilePromise, utilizza un oggetto URLStream come fornitore dei dati.

AIR può leggere i dati in maniera sincrona o asincrona. L'implementazione IFilePromise segnala quale modalità di accesso è supportata restituendo il valore appropriato nella proprietà isAsync . Se è disponibile l'accesso dati asincrono, l'oggetto fornitore di dati deve implementare l'interfaccia IEventDispatcher e inviare gli eventi necessari, ad esempio open , progress e complete .

Potete utilizzare una classe personalizzata, o una delle classi incorporate seguenti, come fornitore di dati per una promessa di file:

  • ByteArray (sincrona)

  • FileStream (sincrona o asincrona)

  • Socket (sincrona)

  • URLStream (asincrona)

Per implementare l'interfaccia IFilePromise, dovete fornire il codice per le funzioni e proprietà seguenti:

  • open():IDataInput : restituisce l'oggetto fornitore di dati da cui vengono letti i dati per il file promesso. L'oggetto deve implementare l'interfaccia IDataInput. Se i dati vengono forniti in modo asincrono, l'oggetto deve anche implementare l'interfaccia IEventDispatcher e inviare gli eventi necessari (vedete Utilizzo di un fornitore di dati asincrono in una promessa di file ).

  • get relativePath():String : fornisce il percorso, incluso il nome file, per il file creato. Il percorso viene risolto in base alla posizione di rilascio scelta dall'utente nell'operazione di trascinamento. Per essere certi che il percorso utilizzi il carattere separatore appropriato per il sistema operativo host, utilizzate la costante File.separator quando specificate percorsi contenenti directory. Per poter impostare il percorso in fase di runtime, potete aggiungere una funzione setter oppure utilizzare un parametro della funzione di costruzione.

  • get isAsync():Boolean : informa il runtime AIR se i dati dell'oggetto fornitore di dati vengono forniti in maniera asincrona o sincrona.

  • close():void : chiamata dal runtime quando i dati sono completamente letti (o un errore impedisce di continuare la lettura). Potete utilizzare questa funzione per ottimizzare le risorse.

  • reportError( e:ErrorEvent ):void : chiamata dal runtime quando si verifica un errore di lettura dei dati.

Tutti i metodi IFilePromise vengono chiamati dal runtime durante un'operazione di trascinamento che coinvolge la promessa di file. In genere, la logica dell'applicazione non deve essere chiamata direttamente da nessuno di questi metodi.

Utilizzo di un fornitore di dati sincrono in una promessa di file

Il modo più semplice per implementare l'interfaccia IFilePromise è utilizzare un oggetto fornitore di dati sincrono, ad esempio un ByteArray o un FileStream sincrono. Nell'esempio seguente, un oggetto ByteArray viene creato, compilato con i dati e restituito quando viene chiamato il metodo 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 ); 
        } 
    } 
}

In pratica, le promesse di file sincrone hanno un'utilità limitata. Se la quantità di dati è piccola, potete facilmente creare un file in una directory temporanea e aggiungere un normale array elenco file agli Appunti di trascinamento. Se, invece, la quantità di dati è grande o generare i dati richiede notevoli risorse di calcolo, è necessario un lungo processo sincrono. I processi sincroni lunghi possono bloccare gli aggiornamenti dell'interfaccia utente per un periodo di tempo ragguardevole durante il quale l'applicazione non è in grado di rispondere. Per evitare questo problema, potete creare un fornitore di dati asincrono basato su un timer.

Utilizzo di un fornitore di dati asincrono in una promessa di file

Quando utilizzate un oggetto fornitore di dati asincrono, la proprietà isAsync di IFilePromise deve essere impostata su true e l'oggetto restituito dal metodo open() deve implementare l'interfaccia IEventDispatcher. Il runtime intercetta eventi alternativi in modo da poter utilizzare oggetti incorporati diversi come fornitore di dati. Ad esempio, gli eventi progress vengono inviati dagli oggetti FileStream e URLStream, mentre gli eventi socketData vengono inviati da oggetti Socket. Il runtime intercetta gli eventi appropriati da tutti questi oggetti.

Gli eventi seguenti guidano il processo di lettura dei dati dall'oggetto fornitore di dati:

  • Event.OPEN: informa il runtime che l'origine dati è pronta.

  • ProgressEvent.PROGRESS: informa il runtime che i dati sono disponibili. Il runtime legge la quantità di dati disponibile dall'oggetto fornitore di dati.

  • ProgressEvent.SOCKET_DATA: informa il runtime che i dati sono disponibili. L'evento socketData viene inviato da oggetti basati su socket. Per altri tipi di oggetti, dovete inviare un evento progress . (Il runtime intercetta entrambi gli eventi per rilevare quando è possibile leggere i dati.)

  • Event.COMPLETE: informa il runtime che tutti i dati sono stati letti.

  • Event.CLOSE: informa il runtime che tutti i dati sono stati letti. (Per questa ragione, il runtime intercetta entrambi gli eventi close e complete .)

  • IOErrorEvent.IOERROR: informa il runtime che si è verificato un errore di lettura dei dati. Il runtime arresta la creazione del file e chiama il metodo close() di IFilePromise.

  • SecurityErrorEvent.SECURITY_ERROR: informa il runtime che si è verificato un errore di sicurezza. Il runtime arresta la creazione del file e chiama il metodo close() di IFilePromise.

  • HTTPStatusEvent.HTTP_STATUS: utilizzato dal runtime, insieme a httpResponseStatus , per essere certi che i dati disponibili rappresentino il contenuto desiderato, piuttosto che un messaggio di errore (ad esempio, una pagina 404). Gli oggetti basati sul protocollo HTTP devono inviare questo evento.

  • HTTPStatusEvent.HTTP_RESPONSE_STATUS: utilizzato dal runtime insieme a httpStatus per essere certi che i dati disponibili rappresentino il contenuto desiderato. Gli oggetti basati sul protocollo HTTP devono inviare questo evento.

Il fornitore di dati deve inviare questi eventi nella sequenza seguente:

  1. evento open

  2. Evento progress o socketData

  3. Evento complete o close

Nota: gli oggetti incorporati, FileStream, Socket e URLStream, inviano gli eventi appropriati automaticamente.

Nell'esempio seguente viene creata una promessa di file utilizzando un fornitore di dati asincrono personalizzato. La classe del fornitore di dati estende ByteArray (per il supporto IDataInput) e implementa l'interfaccia IEventDispatcher. A ogni evento del timer, l'oggetto genera una porzione di dati e invia un evento progress per informare il runtime che i dati sono disponibili. Quando è stato prodotto un numero di dati sufficiente, l'oggetto invia un evento 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 ); 
    } 
} 
}
Nota: poiché la classe AsyncDataProvider nell'esempio estende ByteArray, non può estendere anche EventDispatcher. Per implementare l'interfaccia IEventDispatcher, la classe utilizza un oggetto EventDispatcher interno e inoltra le chiamate al metodo IEventDispatcher a tale oggetto. Potete anche estendere EventDispatcher e implementare IDataInput (o implementare entrambe le interfacce).

L'implementazione IFilePromise asincrona è quasi identica a quella sincrona. Le differenze principali sono che isAsync restituisce true e che il metodo open() restituisce un oggetto dati asincrono:

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