Actionscript 3 AIR(As3)-通过iPad应用程序下载大文件
首先,我想指定我不能发布源代码,因为这个项目太大了。 我正试图在iPad设备上下载一个大文件(500+MB)。 起初我尝试使用URLLoader,但后来我意识到iPad设备的内存资源非常有限。我原以为URLStream会将文件分块下载,使用FileStream我可以将这些分块保存在设备上(如下所示),但我错了,当我尝试下载一个大文件时,设备崩溃,因为设备的RAM不够(更确切地说,这会变得太大:System.privateMemory) 有人知道如何分块下载文件吗?不使用“套接字连接”也可以吗 提前谢谢 编辑: 下面是我使用的代码(注释行是仅在下载文件后关闭FileStream las的版本)Actionscript 3 AIR(As3)-通过iPad应用程序下载大文件,actionscript-3,flash,mobile,air,Actionscript 3,Flash,Mobile,Air,首先,我想指定我不能发布源代码,因为这个项目太大了。 我正试图在iPad设备上下载一个大文件(500+MB)。 起初我尝试使用URLLoader,但后来我意识到iPad设备的内存资源非常有限。我原以为URLStream会将文件分块下载,使用FileStream我可以将这些分块保存在设备上(如下所示),但我错了,当我尝试下载一个大文件时,设备崩溃,因为设备的RAM不够(更确切地说,这会变得太大:System.privateMemory) 有人知道如何分块下载文件吗?不使用“套接字连接”也可以吗 提
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();
}
}
}
正如我在对CSOMAK的评论中所说,我已经使用URLStream分块方法通过AIR为桌面、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(可能更高)下正常工作(虽然我应该测试一下,现在我想起来了)。写得相当快,所以可能会有一些错误,我确实缺少一些东西,但你明白了
如果这不起作用,我们需要您提供一些信息:
fs.close()
之后跟踪file.size/1024/1024+“MB”
,看看它在崩溃之前能走多远System.memory/1024/1024+“MB”,以便我们可以监控内存使用情况
或者,您应该知道,应用程序中的500MB文件将无法执行任何操作。Flash由于其大小而无法加载。我之所以能够处理300MB的视频文件,唯一的原因是我们从磁盘流式传输它们,而不是将整件事存储到内存中。因为我不允许通信根据Josh的回答,出于某种原因,我添加了我的版本作为一个单独的答案。但这在很大程度上是基于他的建议。 GitHub也提供了该代码,网址为: 先决条件-URLStream是一个很棒的类,但它有一个小故障,导致内存泄漏/累积,从而阻止大文件正确加载。我在这里共享的类已经过测试,能够将一系列1.5GB的文件下载到iPad Air 2(64GB)中没有问题。我认为较大的文件也可以,因为它实际上克服了RAM存储限制(在故障修复之前,它在200MB左右崩溃) 这个小故障-在加载字节上复制的原始数据bytearray永远不会被GC处理(如下所述:),因此,解决方法是实现一个使用Josh技术的类,并确保在写入后处理字节 …以下是代码(注意:这仍然是预生产): …我希望这有帮助 干杯
-尼克如果你有权访问服务器端,你可以创建一个脚本,一次发送500MB的文件块。但是,如果设备没有足够的RAM来使用它,为什么你需要下载文件呢?您好,我和您使用的技术几乎相同,唯一的区别是,我只在最后关闭FileStream。我现在正在运行一些测试,但它不可用可能无法工作。文件可能确实保存在应用程序专用内存中。下面是一些跟踪日志:trace(“内存:”,System.totalMemoryNumber/1024/1024,“内存P:”,System.privatemory/1024/1024);结果:内存:20.8125内存P:286.0546875,这是文件下载时的25%。最终结果是文件下载时应用程序崩溃。以下是跟踪日志:内存:21.75390625内存P:359.15625可用内存:9.15234375进度:1查找代码时,您从不附加
fileStream\u outputProgressHandler
到您的urlstream,因此您的waitingForDataToWrite
属性永远不会设置为true,并且在触发COMPLETE
事件之前,您的数据永远不会写入。我一直在努力让它工作这么久!我试图在完全下载的文件上读取字节\写入字节。显然,由于它的巨大尺寸,应用程序正在崩溃内存不足。但是你的读写分块的方法很有魅力!非常感谢启示录!:)我测试了250Mb的文件,它很好地满足了我的需要。请记住,除非是流式传输,否则你将无法将250Mb的文件真正用于任何事情(也就是说,您无法保存一个250MB的字符串并将其全部读取到内存中,但如果您一次只读取10MB,这是完全可以接受的)。这可能会起作用,但内存管理会非常差。请记住,由于AIR 20和iOS 9,您需要一个额外的设置来确保HTTP流量通过-在清单文件中添加一个条目,其中包括:NSAppTransportSecurityNSAllowsArbitraryLoads…]>FYI:上面的代码示例在iPa上有效D2(iOS 8)、iPad3(iOS 9)和iPhone4S(iOS 9)的结果完全一样——1.5GB+的文件加载很好——没有泄漏。在我的例子中,文件是JPEG、PDF和MPEG4-s,下载的文件可以查看,所以逐字节写入似乎工作得很好。
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();
}
}
}
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;
}
}
}