首先,我想指定我不能发布源代码,因为项目很大。 我正在尝试在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();
}
}
}
答案 0 :(得分:3)
正如我在对csomakk的评论中所说,我已经使用URLStream分块方法通过AIR成功下载了300多个MB文件,用于桌面,iOS和Android。
伪代码:
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(可能还会更远。虽然我应该测试一下,现在考虑一下)。这写得相当快,所以可能会有一些错误,我肯定会做一些事情,但你明白了。但是你明白了。
如果这不起作用,我们需要你做一些事情:
file.size / 1024 / 1024 + "MB"
之后追踪fs.close()
并查看崩溃前的距离System.memory / 1024 / 1024 + "MB" after the
fs.close()`以便我们可以监控内存使用情况对于2和3,我们应该只在发生崩溃之前需要最后的跟踪语句。
或者,您应该知道在应用程序中无法对该500MB文件执行任何操作。由于它的大小,Flash根本不会加载它。我设法逃脱300MB视频文件的唯一原因是我们从磁盘流式传输它们,而不是将所有内容存储到内存中。
答案 1 :(得分: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设备。记录:我使用的是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;
}
}
}
......我希望有所帮助!
欢呼声 -Nick