IFilePromise 인터페이스 구현

Adobe AIR 2 이상

URLFilePromise 객체를 사용하여 액세스할 수 없는 리소스에 대해 파일 프로미스를 제공하려면 사용자 정의 클래스에서 IFilePromise 인터페이스를 구현할 수 있습니다. IFilePromise 인터페이스는 파일 프로미스가 드롭된 후 파일에 기록할 데이터에 액세스하기 위해 AIR 런타임에서 사용되는 메서드 및 속성을 정의합니다.

IFilePromise 구현은 AIR 런타임에 파일 프로미스에 대한 데이터를 제공하는 또 다른 객체를 전달합니다. 이 객체는 AIR 런타임이 데이터를 읽기 위해 사용하는 IDataInput 인터페이스를 구현해야 합니다. 예를 들어 IFilePromise를 구현하는 URLFilePromise 클래스는 URLStream 객체를 데이터 공급자로 사용합니다.

AIR는 데이터를 동기적 또는 비동기적으로 읽을 수 있습니다. IFilePromise 구현은 isAsync 속성에 적합한 값을 반환하여 지원되는 액세스 모드를 보고합니다. 비동기 데이터 액세스가 제공되는 경우 데이터 공급자 객체는 IEventDispatcher 인터페이스를 구현하고 open, progresscomplete와 같은 필요한 이벤트를 전달해야 합니다.

사용자 정의 클래스 또는 다음과 같은 기본 제공 클래스 중 하나를 파일 프로미스에 대한 데이터 공급자로 사용할 수 있습니다.

  • ByteArray(동기)

  • FileStream(동기 또는 비동기)

  • Socket(비동기)

  • URLStream(비동기)

IFilePromise 인터페이스를 구현하려면 다음 함수 및 속성에 대한 코드를 제공해야 합니다.

  • open():IDataInput - 프로미스된 파일에 대한 데이터를 읽을 데이터 공급자 객체를 반환합니다. 객체는 IDataInput 인터페이스를 구현해야 합니다. 또한 데이터가 비동기적으로 제공되는 경우 객체는 IEventDispatcher 인터페이스를 구현하고 필요한 이벤트를 전달해야 합니다(파일 프로미스에서 비동기 데이터 공급자 사용 참조).

  • get relativePath():String - 만들어진 파일에 대한 파일 이름을 포함하는 경로를 제공합니다. 경로는 사용자가 드래그 앤 드롭 작업에서 선택한 드롭 위치에 대한 상대 경로로 확인됩니다. 경로에 호스트 운영 체제에 대한 올바른 분리 기호 문자가 사용되도록 하려면 디렉토리를 포함하는 경로를 지정할 때 File.separator 상수를 사용합니다. setter 함수를 추가하거나 constructor 매개 변수를 사용하여 경로를 런타임에 설정하도록 허용할 수 있습니다.

  • get isAsync():Boolean - 데이터 공급자 객체가 데이터를 비동기적으로 제공하는지 아니면 동기적으로 제공하는지를 AIR 런타임에 알립니다.

  • close():void - 데이터를 완전히 읽은 경우 또는 오류로 인해 더 이상 읽을 수 없는 경우 런타임에서 호출됩니다. 이 함수를 사용하여 리소스를 정리할 수 있습니다.

  • reportError( e:ErrorEvent ):void - 데이터를 읽는 동안 오류가 발생하는 경우 런타임에서 호출됩니다.

모든 IFilePromise 메서드는 파일 프로미스와 관련된 드래그 앤 드롭 작업 중에 런타임에서 호출됩니다. 일반적으로 응용 프로그램 논리에서는 이러한 메서드를 직접 호출하지 않아야 합니다.

파일 프로미스에서 동기 데이터 공급자 사용

IFilePromise 인터페이스를 구현하는 가장 간단한 방법은 ByteArray 또는 동기 FileStream과 같은 동기 데이터 공급자 객체를 사용하는 것입니다. 다음 예제에서는 open() 메서드가 호출될 때 ByteArray 객체가 만들어지고 데이터로 채워지며 반환됩니다.

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 이벤트는 소켓 기반 객체에 의해 전달됩니다. 다른 객체 유형의 경우 progress 이벤트를 전달해야 합니다. 런타임에서 두 이벤트를 모두 수신하여 데이터를 읽을 수 있는 경우를 감지합니다.

  • Event.COMPLETE - 데이터를 모두 읽었음을 런타임에 알립니다.

  • Event.CLOSE - 데이터를 모두 읽었음을 런타임에 알립니다. 이를 위해 런타임에서 closecomplete를 모두 수신합니다.

  • IOErrorEvent.IOERROR - 데이터를 읽는 동안 오류가 발생했음을 런타임에 알립니다. 런타임에서 파일 만들기를 중단하고 IFilePromise close() 메서드를 호출합니다.

  • SecurityErrorEvent.SECURITY_ERROR - 보안 오류가 발생했음을 런타임에 알립니다. 런타임에서 파일 만들기를 중단하고 IFilePromise close() 메서드를 호출합니다.

  • HTTPStatusEvent.HTTP_STATUS - 사용 가능한 데이터가 오류 메시지(예: 404 페이지)가 아니라 원하는 내용을 나타내도록 하기 위해 httpResponseStatus와 함께 런타임에서 사용됩니다. HTTP 프로토콜 기반 객체는 이 이벤트를 전달해야 합니다.

  • HTTPStatusEvent.HTTP_RESPONSE_STATUS - 사용 가능한 데이터가 원하는 내용을 나타내도록 하기 위해 httpStatus와 함께 런타임에서 사용됩니다. HTTP 프로토콜 기반 객체는 이 이벤트를 전달해야 합니다.

데이터 공급자는 다음과 같은 순서로 이러한 이벤트를 전달해야 합니다.

  1. open 이벤트

  2. progress 또는 socketData 이벤트

  3. complete 또는 close 이벤트

참고: 기본 제공 객체 FileStream, Socket 및 URLStream은 적합한 이벤트를 자동으로 전달합니다.

다음 예제에서는 사용자 정의 비동기 데이터 공급자를 사용하여 파일 프로미스를 만듭니다. 데이터 공급자 클래스는 ByteArray(IDataInput 지원)를 확장하고 IEventDispatcher 인터페이스를 구현합니다. 각 타이머 이벤트에서 객체는 데이터 청크를 생성하고 데이터를 사용할 수 있음을 런타임에 알리기 위해 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 구현은 동기 구현과 거의 동일합니다. 주요 차이점은 isAsynctrue를 반환하고, 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 ); 
        } 
    } 
}