2

首先我想说明我不能发布源代码,因为这个项目很大。我正在尝试在 iPad 设备上下载一个大文件 (500+ MB)。最初我尝试使用 URLLoader,但后来我意识到 iPad 设备的内存资源非常有限。比我认为 URLStream 会以块的形式下载文件,并且使用 FileStream 我可以将这些块保存在设备上(就像这个AS3:URLStream 将文件保存到桌面?),但我错了,当我尝试下载时设备崩溃了大文件,因为设备的 RAM 不够(更确切地说,这变得太大:System.privateMemory) 有谁知道如何分块下载文件,是否可以不使用“套接字连接”?

提前致谢。

编辑:这是我使用的代码(注释行是 FileStream las 仅在文件下载后关闭的版本。



    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 分块方法通过 AIR for desktop、iOS 和 Android 成功下载了 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

由于出于某种原因,我不允许在 Josh 的回答下发表评论,因此我将我的版本添加为单独的答案。但这在很大程度上是基于他的建议。该代码也可通过 GitHub 获得:https ://github.com/shishenkov/ActionscriptClasses/blob/master/us/flashmx/net/LargeFileLoader.as

先决条件 - 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 设备。备案:我使用的是 Adob​​e 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 回答