实现 IFilePromise 接口

Adobe AIR 2 和更高版本

若要为不能使用 URLFilePromise 对象访问的资源提供文件释放,可以在自定义类中实现 IFilePromise 接口。一旦放置文件释放后,IFilePromise 接口就会定义由 AIR 运行时访问要写入到文件中的数据所用的方法和属性。

注: 由于 JavaScript 语言不支持接口的实现,您只能使用 ActionScript 实现自己的文件承诺逻辑。当然,您可以将包含 ActionScript 类的 SWF 文件导入到使用 <script> 标签的 HTML 页面并以 JavaScript 代码访问这些类。

IFilePromise 实现将为该文件释放提供数据的另一个对象传递到 AIR 运行时。此对象必须实现 AIR 运行时用于读取数据的 IDataInput 接口。例如,可实现 IFilePromise 的 URLFilePromise 类使用 URLStream 对象作为数据提供程序。

AIR 可以同步读取数据,也可以异步读取数据。IFilePromise 实现通过返回 isAsync 属性中的相应值来报告支持哪种访问模式。如果提供异步数据访问,则数据提供程序对象必须实现 IEventDispatcher 接口并调度必要的事件,例如 open progress complete

可使用自定义类或以下内置类之一作为文件释放的数据提供程序:

  • ByteArray(同步)

  • FileStream(同步或异步)

  • Socket(异步)

  • URLStream(异步)

要实现 IFilePromise 接口,必须为下列函数和属性提供代码:

  • open():IDataInput — 从读取的已释放文件的数据返回数据提供程序对象。该对象必须实现 IDataInput 接口。如果异步提供数据,对象还必须实现 IEventDispatcher 接口并调度必要的事件(请参阅 在文件释放中使用异步数据提供程序 )。

  • get relativePath():String — 提供已创建文件的路径,包括文件名。此路径被解析为相对于由拖放操作中用户选择的放置位置。要确保此路径使用适用于主机操作系统的适当分隔符,请在指定包含目录的路径时使用 File.separator 常量。您可以添加 setter 函数或使用构造函数参数来允许在运行时设置路径。

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

实际上,同步文件释放的效用很受限。如果数据量很小,您可以在临时目录中轻松创建一个文件,将普通文件列表数组添加到拖放剪贴板。另一方面,如果数据量很大或生成数据的计算成本很高,则长的同步进程是必需的。长的同步进程会在明显较长的时间内阻止 UI 更新,使应用程序看起来像无响应一样。若要避免此问题,您可以创建一个由计时器驱动的异步数据提供程序。

在文件释放中使用异步数据提供程序

使用异步数据提供程序对象时,IFilePromise isAsync 属性必须为 true ,并且由 open() 方法返回的对象必须实现 IEventDispatcher 接口。运行时侦听多个替代事件,以便可以使用不同的内置对象作为数据提供程序。例如, progress 事件由 FileStream 和 URLStream 对象调度,而 socketData 事件由 Socket 对象调度。运行时从所有这些对象侦听适当的事件。

下列事件推动从数据提供程序对象读取数据的进程:

  • Event.OPEN — 通知运行时数据源已准备好。

  • ProgressEvent.PROGRESS — 通知运行时数据可用。运行时将从数据提供程序对象读取可用的数据量。

  • ProgressEvent.SOCKET_DATA — 通知运行时数据可用。 socketData 事件由基于 Socket 的对象调度。对于其他对象类型,您应调度 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 接口。在每个 timer 事件发生时,对象会生成大量数据,并调度 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 ); 
        } 
    } 
}