Implementação da interface IFilePromise

Adobe AIR 2 e posterior

Para fornecer promessas de arquivo para os recursos que não podem ser acessados com um objeto URLFilePromise, você pode implementar a interface IFilePromise em uma classe personalizada. A interface IFilePromise define os métodos e propriedades usados pelo tempo de execução do AIR para acessar os dados a serem gravados em um arquivo depois que a promessa é solta.

Uma implementação IFilePromise passa outro objeto ao tempo de execução do AIR que fornece os dados para a promessa de arquivo. Esse objeto deve implementar a interface IDataInput, que o tempo de execução do AIR usa para ler os dados. Por exemplo, a classe URLFilePromise, que implementa IFilePromise, usa um objeto URLStream como fornecedor dos dados.

O AIR pode ler os dados de forma síncrona ou assíncrona. A implementação IFilePromise informa qual modo de acesso é suportado ao devolver o valor apropriado na propriedade isAsync . Se for fornecido acesso aos dados assíncronos, o objeto do fornecedor de dados deve implementar a interface IEventDispatcher e despachar os eventos necessários, como open , progress e complete .

Você pode usar uma classe personalizada ou uma das classes incorporadas seguintes, como fornecedor de dados para uma promessa de dados:

  • ByteArray (síncrono)

  • FileStream (síncrono ou assíncrono)

  • Socket (assíncrono)

  • URLStream (assíncrono)

Para implementar a interface IFilePromise, você deve fornecer o código para as funções e propriedades seguintes:

  • open():IDataInput — Devolve o objeto do fornecedor de dados dos quais os dados para o arquivo prometido são lidos. O objeto deve implementar a interface IDataInput. Se os dados são fornecidos de forma assíncrona, o objeto também deve implementar a interface IEventDispatcher e despachar os eventos necessários (consulte Uso do fornecedor de dados assíncrono em uma promessa de arquivo ).

  • get relativePath():String — Fornece o caminho, incluindo nome de arquivo, para o arquivo criado. O caminho é resolvido em relação ao local de soltar escolhido pelo usuário na operação de arrastar e soltar. Para ter certeza de que o caminho usa o caractere separador adequado para o sistema operacional de host, use a constante File.separator ao especificar os caminhos que contêm diretórios. Você pode adicionar uma função setter ou usar um parâmetro constructor para permitir que o caminho seja definido no tempo de execução.

  • get isAsync():Boolean — Informa o tempo de execução do AIR se o objeto do fornecedor de dados fornece seus dados de forma síncrona ou assíncrona.

  • close():void — Chamado pelo tempo de execução quando os dados são inteiramente lidos (ou um erro impede novas leituras). Você pode usar essa função para os recursos de limpeza.

  • reportError( e:ErrorEvent ):void — Chamando pelo tempo de execução quando ocorre um erro ao ler os dados.

Todos os métodos IFilePromise são chamados pelo tempo de execução durante uma operação de arrastar e soltar envolvendo a promessa de arquivo. Em geral, sua lógica de aplicativo não deve chamar nenhum desses métodos diretamente.

Uso do fornecedor de dados síncrono em uma promessa de arquivo

A forma mais simples de implementar a interface IFilePromise é usar um objeto de fornecedor de dados síncrono, como ByteArray ou um FileStream síncrono. No exemplo a seguir, é criado um objeto ByteArray, preenchido com dados e retornado quando se chama o método 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 ); 
        } 
    } 
}

Na prática, as promessas de arquivo síncronas têm utilidade limitada. Se a quantidade de dados for pequena, você poderia facilmente criar um arquivo em um diretório temporário e acrescentar uma matriz normal da lista de arquivos à área de transferência de arrastar e soltar. Por outro lado, se a quantidade de dados for grande ou for caro gerar os dados de forma computacional, é necessário um processo síncrono longo. Os processos síncronos longos podem bloquear as atualizações de UI por um tempo notável e fazer com que seu aplicativo pareça não responder. Para evitar este problema, você pode criar um fornecedor de dados assíncronos orientado por um cronômetro.

Uso do fornecedor de dados assíncrono em uma promessa de arquivo

Ao usar um objeto de fornecedor de dados assíncrono, a propriedade isAsync do IFilePromise deve ser true e o objeto devolvido pelo método open() deve implementar a interface IEventDispatcher. O tempo de execução ouve os vários eventos alternativos de forma que se possam usar objetos incorporados diferentes como fornecedor de dados. Por exemplo, os eventos progress são despachados pelos objetos FileStream e URLStream, ao passo que os eventos socketData são despachados pelos objetos Socket. O tempo de execução ouve os eventos apropriados de todos esses objetos.

Os eventos a seguir orientam o processo de leitura de dados do objeto do fornecedor de dados:

  • Event.OPEN — Informa ao tempo de execução que a fonte de dados está pronta.

  • ProgressEvent.PROGRESS — Informa ao tempo de execução que os dados estão disponíveis. O tempo de execução vai ler a quantidade de dados disponíveis do objeto do fornecedor de dados.

  • ProgressEvent.SOCKET_DATA — Informa ao tempo de execução que os dados estão disponíveis. O evento socketData é despachado pelos objetos baseados em soquete. Para os outros tipos de objeto, despache um evento progress . (O tempo de execução ouve os dois eventos para detectar quando os dados podem ser lidos.)

  • Event.COMPLETE — Informa ao tempo de execução que os dados foram todos lidos.

  • Event.CLOSE — Informa ao tempo de execução que os dados foram todos lidos. (O tempo de execução ouve close e complete para essa finalidade.)

  • IOErrorEvent.IOERROR — Informa ao tempo de execução que ocorreu um erro ao ler os dados. O tempo de execução aborta a criação do arquivo e chama o método close() do IFilePromise.

  • SecurityErrorEvent.SECURITY_ERROR — Informa ao tempo de execução que ocorreu um erro de segurança. O tempo de execução aborta a criação do arquivo e chama o método close() do IFilePromise.

  • HTTPStatusEvent.HTTP_STATUS — Usado, junto com httpResponseStatus , pelo tempo de execução para garantir que os dados disponíveis representem o conteúdo desejado, em vez de uma mensagem de erro (como uma página 404). Os objetos baseados no protocolo HTTP devem despachar esse evento.

  • HTTPStatusEvent.HTTP_RESPONSE_STATUS — Usado, junto com httpStatus , pelo tempo de execução para garantir que os dados disponíveis representam o conteúdo desejado. Os objetos baseados no protocolo HTTP devem despachar esse evento.

O fornecedor de dados deve despachar esses eventos na sequência a seguir:

  1. evento open

  2. Eventos progress ou socketData

  3. Evento complete ou close

Nota: Os objetos incorporados, FileStream, Socket e URLStream, despacham os eventos apropriados automaticamente.

O exemplo a seguir cria uma promessa de arquivo usando um fornecedor de dados personalizado e assíncrono. A classe do fornecedor de dados estende ByteArray (para o suporte do IDataInput) e implementa a interface IEventDispatcher. No evento de cada cronômetro, o objeto gera um pouco de dados e despacha um evento progress para informar ao tempo de execução que os dados estão disponíveis. Quando forem produzidos dados suficientes, o objeto despacha um evento completo.

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: Como a classe AsyncDataProvider no exemplo estende ByteArray, ela não pode estender também o EventDispatcher. Para implementar a interface IEventDispatcher, a classe usa um objeto EventDispatcher interno e encaminha as chamadas do método IEventDispatcher para aquele objeto interno. Você também poderia estender o EventDispatcher e implementar IDataInput (ou implementar as duas interfaces).

A implementação assíncrona de IFilePromise é quase idêntica à implementação síncrona. As principais diferenças são que isAsync retorna true e que o método open() retorna um objeto de dados assíncrono:

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