2

最初に、プロジェクトが巨大であるため、ソース コードを投稿できないことを明記したいと思います。iPad デバイスに大きなファイル (500 MB 以上) をダウンロードしようとしています。最初に URLLoader を試してみましたが、iPad デバイスのメモリに関するリソースが非常に限られていることに気付きました。URLStream がファイルをチャンクでダウンロードし、FileStream を使用してこのチャンクをデバイスに保存できると思っていたよりも (このAS3: URLStream Saving files to desktop?のように)、しかし私は間違っていました。デバイスのRAMが十分でないため、大きなファイル(より正確には、これは大きくなりすぎます:System.privateMemory)ファイルをチャンクでダウンロードする方法を知っている人はいますか?「ソケット接続」を使用せずに可能ですか?

前もって感謝します。

編集:これが私が使用するコードです(コメント行は、ファイルがダウンロードされた後にのみFileStreamが閉じられたバージョンです.



    package components.streamDownloader
    {
        import flash.events.Event;
        import flash.events.EventDispatcher;
        import flash.events.IOErrorEvent;
        import flash.events.OutputProgressEvent;
        import flash.events.ProgressEvent;
        import flash.events.SecurityErrorEvent;
        import flash.filesystem.File;
        import flash.filesystem.FileMode;
        import flash.filesystem.FileStream;
        import flash.net.URLRequest;
        import flash.net.URLStream;
        import flash.system.System;
        import flash.utils.ByteArray;


    /**
     * 
     */
    public class StreamDownloader extends EventDispatcher
    {

        [Event(name="DownloadComplete", type="com.tatstyappz.net.DownloadEvent")]

        [Event(name="Error", type="com.tatstyappz.net.DownloadEvent")]


        //--------------------------------------------------------------------------
        //
        //  Constructor
        //
        //--------------------------------------------------------------------------

        public function StreamDownloader()
        {

        }


        //--------------------------------------------------------------------------
        //
        //  Variables
        //
        //--------------------------------------------------------------------------

        private var file:File;

        //private var fileStream:FileStream;

        private var urlRequest:URLRequest;

        private var urlStream:URLStream;

        private var waitingForDataToWrite:Boolean = false;


        //--------------------------------------------------------------------------
        //
        //  API
        //
        //--------------------------------------------------------------------------

        public function download(urlRequest:URLRequest, file:File):void {


            init();

            this.urlRequest = urlRequest;
            this.file = file;
            //fileStream.open(file, FileMode.WRITE);
            urlStream.load(urlRequest);
        }   


        //--------------------------------------------------------------------------
        //
        //  Event handlers
        //
        //--------------------------------------------------------------------------    

        //----------------------------------
        //  urlStream events
        //----------------------------------

        protected function urlStream_openHandler(event:Event):void
        {
            waitingForDataToWrite = false;
            dispatchEvent(event.clone());
        }

        protected function urlStream_progressHandler(event:ProgressEvent):void
        {


            trace("MEMORY:", System.totalMemoryNumber / 1024 / 1024, "MEMORY P:", System.privateMemory / 1024 / 1024, "FREE MEMORY:", System.freeMemory / 1024 / 1024, "PROGRESS:", event.bytesLoaded / event.bytesTotal );




            if(waitingForDataToWrite){
                writeToDisk();
            }       
        }

        protected function urlStream_completeHandler(event:Event):void
        {
            if(urlStream.bytesAvailable > 0)
            {
                writeToDisk();
            }
            //fileStream.close();

            destory();

            dispatchEvent(event.clone());

            // dispatch additional DownloadEvent
            dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.DOWNLOAD_COMPLETE, urlRequest, file));        
        }

        protected function urlStream_securityErrorHandler(event:SecurityErrorEvent):void
        {
            dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.ERROR, urlRequest, file, event.errorID.toString()));
            destory();
        }

        protected function urlStream_ioErrorHandler(event:IOErrorEvent):void
        {
            dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.ERROR, urlRequest, file, event.errorID.toString()));
            destory();
        }   


        //----------------------------------
        //  fileStream events
        //----------------------------------

        protected function fileStream_outputProgressHandler(event:OutputProgressEvent):void
        {
            waitingForDataToWrite = true;
        }   

        protected function fileStream_ioErrorHandler(event:IOErrorEvent):void
        {
            dispatchEvent(new StreamDownloadEvent(StreamDownloadEvent.ERROR, urlRequest, file, event.errorID.toString()));
            destory();
        }   


        //--------------------------------------------------------------------------
        //
        //  Utils
        //
        //--------------------------------------------------------------------------

        private function init():void
        {
            urlStream = new URLStream();
            //fileStream = new FileStream();

            urlStream.addEventListener(Event.OPEN, urlStream_openHandler);
            urlStream.addEventListener(ProgressEvent.PROGRESS, urlStream_progressHandler); 
            urlStream.addEventListener(Event.COMPLETE, urlStream_completeHandler);
            urlStream.addEventListener(IOErrorEvent.IO_ERROR, urlStream_ioErrorHandler);
            urlStream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, urlStream_securityErrorHandler);

            //fileStream.addEventListener(OutputProgressEvent.OUTPUT_PROGRESS, fileStream_outputProgressHandler)
            //fileStream.addEventListener(IOErrorEvent.IO_ERROR, fileStream_ioErrorHandler);        
        }

        private function destory():void
        {
            urlStream.removeEventListener(Event.OPEN, urlStream_openHandler);
            urlStream.removeEventListener(ProgressEvent.PROGRESS, urlStream_progressHandler); 
            urlStream.removeEventListener(Event.COMPLETE, urlStream_completeHandler);
            urlStream.removeEventListener(IOErrorEvent.IO_ERROR, urlStream_ioErrorHandler);
            urlStream.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, urlStream_securityErrorHandler);

            //fileStream.removeEventListener(OutputProgressEvent.OUTPUT_PROGRESS, fileStream_outputProgressHandler)
            //fileStream.removeEventListener(IOErrorEvent.IO_ERROR, fileStream_ioErrorHandler); 

            urlStream = null;
            //fileStream = null;
        }

        private function writeToDisk():void {
            /*var fileData:ByteArray = new ByteArray();
            urlStream.readBytes(fileData, 0, urlStream.bytesAvailable);
            fileStream.writeBytes(fileData,0,fileData.length);
            waitingForDataToWrite = false;*/

            var bytes:ByteArray = new ByteArray();
            urlStream.readBytes( bytes );

            var fs:FileStream = new FileStream();
            fs.open( file, FileMode.APPEND );
            fs.writeBytes( bytes );
            fs.close();
        }




    }
    }


4

2 に答える 2

4

csomakk へのコメントで述べたように、URLStream チャンキング メソッドを使用して、デスクトップ、iOS、および Android 用の AIR から 300 MB 以上のファイルを正常にダウンロードしました。

擬似コード:

var stream:URLStream = new URLStream();
stream.addEventListener( PROGRESS, progressHandler );
stream.addEventListener( COMPLETE, completeHandler );
stream.load( url );

private function progressHandler( e:ProgressEvent ):void {
    this.writeDataToDisk();
}

private function completeHandler( e:Event ):void {
    this.writeDataToDisk();
}

private function writeDataToDisk():void {
    var bytes:ByteArray = new ByteArray();
    this.stream.readBytes( bytes );

    var fs:FileStream = new FileStream();
    fs.open( file, FileMode.APPEND );
    fs.writeBytes( bytes );
    fs.close();
}

その基本的なロジックは機能し、300MB までは問題なく機能します (おそらくそれ以上です。今考えてみると、それをテストする必要がありましたが)。これはかなり急いで書いたので、間違いがあるかもしれません。

これがうまくいかない場合は、いくつかのことをお願いします。

  1. エラーを投稿する
  2. file.size / 1024 / 1024 + "MB"の後にトレースアウトし、fs.close()クラッシュする前にどれだけ到達するかを確認します
  3. System.memory / 1024 / 1024 + "MB" after thefs.close()` をトレースして、メモリ使用量を監視できるようにします

2 と 3 については、クラッシュが発生する前の最後のトレース ステートメントのみが必要です。

または、アプリケーションでその 500MB のファイルを使用して何もできないことを知っておく必要があります。サイズが大きいため、Flash ではロードされません。私が 300MB のビデオ ファイルをなんとか持ちこたえた唯一の理由は、すべてをメモリに保存するのではなく、ディスクからストリーミングしていたからです。

于 2013-01-30T00:36:46.920 に答える
1

何らかの理由でジョシュの回答にコメントすることは許可されていないため、別の回答として自分のバージョンを追加しています。しかし、それは彼の提案に大きく基づいています。このコードは、 https ://github.com/shishenkov/ActionscriptClasses/blob/master/us/flashmx/net/LargeFileLoader.as の GitHub でも入手できます。

前提条件-URLStreamは素晴らしいクラスですが、メモリリーク/ビルドアップを引き起こし、大きなファイルを適切にロードできないという問題があります。ここで共有しているクラスはテスト済みで、一連の 1.5GB ファイルを iPad Air 2 (64GB) に問題なくダウンロードできました。RAMストレージの制限を実際に克服しているため、より大きなファイルも問題ないと思います(グリッチ修正前は約200MBでクラッシュしていました)。

グリッチ - 読み込まれたバイトをコピーする生データのバイト配列が GC によって破棄されることはありません (ここに記載されているように: http://blogs.splunk.com/2007/11/16/flashas3-urlstream-memory-leak/ )そのため、回避策は、Josh の手法を使用するクラスを実装し、書き込み後にバイトが確実に破棄されるようにすることです。

...コードは次のとおりです(注:これはまだ本番前です):

package us.flashmx.net 
{
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.HTTPStatusEvent;
    import flash.events.IEventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.filesystem.File;
    import flash.filesystem.FileMode;
    import flash.filesystem.FileStream;
    import flash.net.URLRequest;
    import flash.net.URLStream;
    import flash.system.System;
    import flash.utils.ByteArray;

    /**
     * ...
     * @author  Nick Shishenkov <n@vc.am>
     */
    public class LargeFileLoader extends EventDispatcher 
    {
        private var _url:String             = "";
        private var _filePath:String        = "";
        private var _fileStream:FileStream  = new FileStream;
        private var _urlStream:URLStream    = new URLStream;
        private var _localFile:File;
        private var _bytesLoaded:Number;

        public function LargeFileLoader() 
        {
            super(null);

            //
            _urlStream.addEventListener(Event.OPEN, _onOpen, false, int.MIN_VALUE, true);
            _urlStream.addEventListener(ProgressEvent.PROGRESS, _onProgress, false, int.MIN_VALUE, true);
            _urlStream.addEventListener(Event.COMPLETE, _onComplete, false, int.MIN_VALUE, true);
            _urlStream.addEventListener(IOErrorEvent.IO_ERROR, _onError, false, int.MIN_VALUE, true);
            _urlStream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, _onSecurityError, false, int.MIN_VALUE, true);
            _urlStream.addEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, _onHTTPStatus, false, int.MIN_VALUE, true);
            _urlStream.addEventListener(HTTPStatusEvent.HTTP_STATUS, _onHTTPStatus, false, int.MIN_VALUE, true);
        }

        private function _onHTTPStatus(e:HTTPStatusEvent):void 
        {
            dispatchEvent(e.clone());
        }

        public function load(remoteURL:String, localPath:String, overwrite:Boolean = true):void
        {
            _url        = remoteURL;
            _filePath   = localPath;
            //
            _localFile      = new File(_filePath);
            _bytesLoaded    = 0;

            //
            if (overwrite && _localFile.exists)
            {
                _localFile.deleteFile();
            }
            //
            _urlStream.load(new URLRequest(url));
            _fileStream.open(_localFile, FileMode.APPEND);
        }

        private function _onOpen(e:Event):void 
        {
            dispatchEvent(e.clone());
        }

        private function _onSecurityError(e:SecurityErrorEvent):void 
        {
            dispatchEvent(e.clone());
        }

        private function _onError(e:IOErrorEvent):void 
        {
            dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, e.text));
        }

        private function _onProgress(e:ProgressEvent):void 
        {
            //
            trace(" -> _onProgress: " + _urlStream.length + " | " + e.bytesLoaded + " / " + e.bytesTotal);
            //
            _writeStreamBytes();
            //
            dispatchEvent(new ProgressEvent(ProgressEvent.PROGRESS, false, false, e.bytesLoaded, e.bytesTotal));
        }

        private function _onComplete(e:Event):void 
        {
            _writeStreamBytes();
            //
            dispatchEvent(new Event(Event.COMPLETE));
        }

        private function _writeStreamBytes():void
        {
            var bytes:ByteArray = new ByteArray();
            _urlStream.readBytes( bytes );
            _fileStream.writeBytes( bytes );

            //
            _bytesLoaded    += bytes.length;

            //clear buffer (if the array stays non-null it will lead to a memmory leak
            bytes   = null;

        }

        public function get url():String 
        {
            return _url;
        }

        public function get filePath():String 
        {
            return _filePath;
        }

        public function get bytesLoaded():Number 
        {
            //_localFile.size;
            return _bytesLoaded;
        }


        public function dispose():void
        {
            try{ _fileStream.close(); }catch (err:Error){};

            //
            try{ _urlStream.close(); }catch (err:Error){};

            //
            _urlStream.removeEventListener(Event.OPEN, _onOpen);
            _urlStream.removeEventListener(ProgressEvent.PROGRESS, _onProgress);
            _urlStream.removeEventListener(Event.COMPLETE, _onComplete);
            _urlStream.removeEventListener(IOErrorEvent.IO_ERROR, _onError);
            _urlStream.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, _onSecurityError);
            _urlStream.removeEventListener(HTTPStatusEvent.HTTP_RESPONSE_STATUS, _onHTTPStatus);
            _urlStream.removeEventListener(HTTPStatusEvent.HTTP_STATUS, _onHTTPStatus);

            //
            _urlStream  = null;
            _fileStream = null;

            //
            System.gc();
        }
    }

}

Scout CC でいくつかのデバイス テストを実行しましたが、メモリは常にダウンしたままです (ビルドアップはまったくありません)。今週後半に、いくつかの古い iOS デバイスをテストする予定です。記録のために:私はAdobe AIR 24.0.0.180を使用しています

使用例は次のとおりです。

package us.flashmx.net 
{
    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.events.ProgressEvent;

    /**
     * ...
     * @author ...
     */
    public class LargeFileLoader_DEMO extends DisplayObject 
    {
        private var _largeFilesLoader:LargeFileLoader;

        public function LargeFileLoader_DEMO() 
        {
            super();
            //
            init_largeFilesLoader("http://A.Large.File.URL/", "/The/Absolute/Local/Path");
        }

        public function dispose_largeFilesLoader():void
        {
            //
            if (_largeFilesLoader != null)
            {
                //clear listeners
                _largeFilesLoader.removeEventListener(ProgressEvent.PROGRESS, _onFileLoaderProgress);
                _largeFilesLoader.removeEventListener(Event.COMPLETE, _onFileLoaderComplete);
                //dispose
                _largeFilesLoader.dispose();
                //free mem
                _largeFilesLoader   = null;
            }           
        }

        private function init_largeFilesLoader(fURL:String, fPath:String):void
        {
            //
            _largeFilesLoader   = new LargeFileLoader;

            //
            _largeFilesLoader.addEventListener(ProgressEvent.PROGRESS, _onFileLoaderProgress, false, int.MIN_VALUE, true);
            _largeFilesLoader.addEventListener(Event.COMPLETE, _onFileLoaderComplete, false, int.MIN_VALUE, true);

            //
            _largeFilesLoader.load(fURL, fPath);
        }

        private function _onFileLoaderComplete(e:Event):void 
        {
            trace("All done!");
            dispose_largeFilesLoader();
        }

        private function _onFileLoaderProgress(e:ProgressEvent):void 
        {
            _largeFilesLoader.bytesLoaded;
        }
    }

}

...それが役立つことを願っています!

乾杯 - ニック

于 2017-01-04T14:15:48.740 に答える