fzip: zipping a file takes full memory - actionscript-3

I'm using fzip to zip the contents of a folder. The folder contains 4 files of 500MB, the problem is that this uses all of my computers memory. Is there a way to prevent this? And lock the memory at 20% or something?
this is my code:
public function packageFileParser(filesToZip:Array):void {
for (var i:uint = 0; i < filesToZip.length; i++) {
if (filesToZip[i]["isDirectory"] == true) {
packageFileParser(filesToZip[i].getDirectoryListing());
} else {
if (filesToZip[i]["isHidden"] == false) {
var byteLoader:UrlURLLoader = new UrlURLLoader();
byteLoader.dataFormat = URLLoaderDataFormat.BINARY;
byteLoader.addEventListener (flash.events.Event.COMPLETE, urlLoaderCompleteHandler);
var fileRequest:URLRequest = new URLRequest ("/"+filesToZip[i]["nativePath"]);
byteLoader.load (fileRequest);
}
}
}
}
private function urlLoaderCompleteHandler(event:flash.events.Event):void {
var saveZip : Function = function(zip : FZip) : void {
var out : ByteArray = new ByteArray();
zip.serialize(out);
var fs : FileStream = new FileStream;
targetFile = File.desktopDirectory.resolvePath(zipName);
fs.open(targetFile, FileMode.WRITE);
fs.writeBytes(out);
out.clear();
fs.close();
};
var fullpath:String = event.target.urlRequest.url;
var filename:String = fullpath.substr(fullpath.lastIndexOf("/")+1,fullpath.length);
var filepath:String = fullpath.substr(0,fullpath.lastIndexOf("/")+1);
filepath = filepath.split("/Users/Thibaut/Desktop/testfolder").join("");
zip.addFile(filepath+filename, event.target.data);
saveZip(zip);
}

AFAIK, FZip library creates entire archive in memory and then saves it to disk. There's nothing you can do except to change library. Try airxzip from coltware, it served me well (I think there was no big memory loads even on large archives, because they were flushed to disk while adding files to them.)
Also, if you're using AIR capabilities (FileStream class), you should add "air" tag to the question, it changes a lot compared to plain AS3.

Related

How do I write a huge bytearray to file progressively and avoid that dreaded memory error

I'm working on an Air project that downloads huge (zip) files, then unzips them and finally deletes the original .zip file it downloaded.
Everything's running smoothly except when I want to write the unzipped file to disk.
Trouble is, I keep running into the issue of this little monster of an error.
Error: Error #1000: The system is out of memory.
Here's the part of the code that is giving me grief. This works perfectly when the file is small (tested on a 3 MB file) but as soon as I try one of my large files (458 MB), I get the error.
private function unzipFile()
{
var pulledFile:FZipFile;
var index:int = 0;
var chunkSize:int = 1000000;
Console.log("Unzipping file...");
var zip:FZip = new FZip();
zip.addEventListener(Event.COMPLETE, onUnzipComplete);
var fileStream:FileStream = new FileStream();
zip.load(new URLRequest(urlToUnzip));
function onUnzipComplete(e:Event)
{
pulledFile = zip.getFileAt(0);
var file:File = File.documentsDirectory.resolvePath(pulledFile.filename);
fileStream.openAsync(file, FileMode.WRITE);
writeChunk();
}
function writeChunk()
{
var bytesToGet:int = pulledFile.content.bytesAvailable;
if (bytesToGet > chunkSize)
bytesToGet = chunkSize;
Console.log("Writing chunk " + (int((pulledFile.content.length - pulledFile.content.bytesAvailable) / chunkSize) + 1) + " of " + (int(pulledFile.content.length / chunkSize) + 1));
var fileData:ByteArray = new ByteArray();
pulledFile.content.readBytes(fileData, 0, bytesToGet);
fileStream.writeBytes(fileData, 0, fileData.length);
if (index < pulledFile.content.bytesAvailable)
{
writeChunk();
index += bytesToGet;
}
else
{
fileStream.close();
Console.log("Unzipping complete");
}
}
}
The code crashes specifically on the line
var bytesToGet:int = pulledFile.content.bytesAvailable;
How do I still progressively write content to a file without knowing how many bytes are available to me? If I don't have access to the bytesAvailable property, how do I know I'm done writing?
I'm using the FZip library for decompression.
This was tough, but I figured it out. The trick is to use the "serialize" method of the FZip and avoid touching the properties of the content ByteArrayaltogether.
Here's the final code that works.
private function unzipFile()
{
var pulledFile:FZipFile;
Console.log("Decompressing file...");
var zip:FZip = new FZip();
zip.addEventListener(Event.COMPLETE, onUnzipComplete);
var fileStream:FileStream = new FileStream();
zip.load(new URLRequest(urlToUnzip));
function onUnzipComplete(e:Event)
{
pulledFile = zip.getFileAt(0);
var file:File = File.documentsDirectory.resolvePath("Easywatch/" + pulledFile.filename);
fileStream.open(file, FileMode.WRITE);
zip.serialize(fileStream, false);
fileStream.close();
Console.log("Decompression complete");
}
}

AS3 Load Image Using Path From Text File

i use txt file that contain paths.
after loaded txt file and split to path array
then load img file from array path
but i get err in loading img
please help me
sample code:
var imglst:Array=new Array();
var lodimg:Loader=new Loader();
var lodtxt:URLLoader=new URLLoader();
lodtxt.load(new URLRequest("imglst.txt"));
lodtxt.addEventListener(Event.COMPLETE,onL_C);
function onL_C(e:Event)
{
var t:Array=new Array();
t=e.target.data.split(/\n/);
var s:URLRequest=new URLRequest(t[0].toString());
trace(t[0]);
lodimg.load(s);
}
lodimg.contentLoaderInfo.addEventListener(Event.COMPLETE,onL_Cimg);
function onL_Cimg(e:Event)
{
var i:Bitmap=new Bitmap();
i=Bitmap(lodimg.content);
i.width=100;
i.height=100;
addChild(i);
trace("OK");
}
Are you loading images from a different website from your own? Does the other website(s) have a crossdomain.xml to allow permissions of loading their content? Usually Flash will give you a "security error" as an event but your code isn't listening for such an event so your program is not aware of any such problems... look on google how to handle AS3 errors.
Anyways a workaround is to just load the bytes of file using URLloader and on completion you then only use Loader to decode the bytes into pixel colours.
import flash.utils.ByteArray;
var imglst : Array;
var lodimg : Loader;
//var lodtxt : URLLoader = new URLLoader();
var lodURL:URLLoader = new URLLoader();
var img_bytes : ByteArray = new ByteArray();
lodURL.load(new URLRequest("http://yoursite/imglst.txt"));
lodURL.addEventListener(Event.COMPLETE,onL_C);
function onL_C (e:Event)
{
//# Since load complete no need to keep listening for that
lodURL.removeEventListener(Event.COMPLETE,onL_C);
var t:Array=new Array();
t=e.target.data.split(/\n/);
var s:URLRequest=new URLRequest(t[0].toString());
trace(t[0]);
//lodimg.contentLoaderInfo.addEventListener(Event.COMPLETE,onL_Cimg);
//lodimg.load(s);
//# Now we know path so we load those file bytes
lodURL.dataFormat = URLLoaderDataFormat.BINARY;
lodURL.load(s); lodURL.addEventListener(Event.COMPLETE, onBytes_C);
}
function onBytes_C (e:Event)
{
//# on complete load of bytes...
trace("got bytes");
img_bytes = lodURL.data;
//trace("Image bytes total : " + img_bytes.length);
img_bytes.position = 0; //avoid "end of file" error
lodimg = new Loader();
lodimg.contentLoaderInfo.addEventListener(Event.COMPLETE,onL_Cimg);
lodimg.loadBytes(img_bytes); //we already have data so this will be fast
}
function onL_Cimg (e:Event)
{
var i:Bitmap = new Bitmap();
i = lodimg.content as Bitmap;
i.width=100;
i.height=100;
addChild(i);
trace("OK");
}

actionscript 3.0 - p2p filesharing troubles (max upload file size)

i've designed a p2p apliccation which can transfer files without a server. and everything works fine. i can transfer files between peers. but as it turns out if file size is greater than 16mb (coz the biggest file i could transfer had a size of 15810 kb) it does't transfer to peer. this is the code i'm using:
private function browseFile(farIds:String = ""):void {
fIds = farIds;
file = new FileReference();
file.addEventListener(Event.SELECT, selectHandler);
file.browse();
}
private function selectHandler(event:Event):void {
var btn = getChild("browseFile_" + fIds)
if (btn && btn.alpha) btn.alpha = 0.5;
file = FileReference(event.target);
file.addEventListener(ProgressEvent.PROGRESS, progressHandler);
file.addEventListener(Event.COMPLETE, completeHandler);
file.load();
}
private function progressHandler(event:ProgressEvent):void{
ExternalInterface.call("fileLoadProgress", event.target.name, event.bytesTotal, event.bytesLoaded)
}
private function completeHandler(event:Event):void{
ExternalInterface.call("onFileLoaded")
var fileData:Object = new Object();
fileData.file = event.target.data
fileData.name = event.target.name;
var btn = getChild("browseFile_" + fIds)
if (btn && btn.alpha) btn.alpha = 1;
sendSomeData(fileData, fIds, "receiveFile");
}
public function receiveFile(info:Object, peerID:String):void{
ExternalInterface.call("alert", "receivedFile")
}
private function sendSomeData(data,farIds:String,func:String = "receiveSomeData"):void{
for(var id:String in sendStreams){
sendStreams[id].send(func, data, myPeerID);
}
}
can you tell me how can i allow transferring all files of any sizes?
thanks for your help!
You can split the file in chunks of say 8KB, and send them one by one. However you must check for the order of received chunks and any possible losses on the way.

AS3 multiple file download, check when completed?

I am looping through a small array of file names to grab from a remote server and save locally. This bit works OK.
The problem is, I need to run my next function when the downloads are complete.
Currently, my function, lets call it step2(), gets called before all the FileStreams can finish (infact, its called before that function even starts!)
I read that there is a complete event listener when using openAsynch, but I need to check that all files, e.g. 3 remote swfs) have been written and then start step2() function.
How can I go about this?
Code below:
function onPlaylistComplete(e:Event)
{
var myArray:Array = new Array();
for each(var i in list_of_files)
{
myArray.push(i);
}
for(i = 0; i < myArray.length; i++)
{
swfItem = myArray[i];
var urlString:String = site_url + myArray[i];
var urlReq:URLRequest = new URLRequest(urlString);
var urlStream:URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE, urlStreamComplete(swfItem));
urlStream.load(urlReq);
}
function urlStreamComplete(swfItem):Function
{
var downloadSwf:Function = function (event:Event):void {
var fileData:ByteArray = new ByteArray();
var stream = event.target;
stream.readBytes(fileData, 0, stream.bytesAvailable);
try {
var f : File = File.documentsDirectory.resolvePath(swfItem);
var fs : FileStream = new FileStream();
fs.addEventListener(Event.COMPLETE, fileCompleteHandler);
fs.openAsync(f, FileMode.WRITE);
fs.writeBytes(fileData);
}
catch (err : Error) {
trace(err.message);
}
}
return downloadSwf;
}
function fileCompleteHandler(e:Event):void {
e.target.close();
}
step2(); // this seems to run before UrlStreamComplete()
}
Problem with your for loop it will never work with Async model. so better need work to with recursive function.
downloadAllFiles method second arg that is function called when all download are completed.Then you can call step2() or whatever may be.
Make sure if any problem while download file like IOErrorEvent,etc...
downloadAllFiles(myArray,function():void
{
step2();
});
function downloadAllFiles(myArray:Array, complete:Function):void
{
if(myArray && myArray.length == 0)
{
if(complete)
complete.call();
return;
}
swfItem = myArray.shift();
var urlString:String = site_url + swfItem;
var urlReq:URLRequest = new URLRequest(urlString);
var urlStream:URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE, function (event:Event):void
{
var fileData:ByteArray = new ByteArray();
var stream = event.target;
stream.readBytes(fileData, 0, stream.bytesAvailable);
try {
var f : File = File.documentsDirectory.resolvePath(swfItem);
var fs : FileStream = new FileStream();
fs.addEventListener(Event.CLOSE, function(closeEvent:Event):void
{
downloadAllFiles(myArray,complete);
return;
});
fs.openAsync(f, FileMode.WRITE);
fs.writeBytes(fileData);
}
catch (err : Error)
{
trace(err.message);
}
});
urlStream.load(urlReq);
}
Can't you just move step2(); at the end of fileCompleteHandler function?
Because at this point step2(); is called right after going through for(i = 0; i < myArray.length; i++)
loop (Which is way to early).
What you are missing is a way to check that all files from your list are loaded, 3vliguy is right and your step2(); should be in the complete handler but, not alone, you have to also test if ALL files are loaded. you can do it in various ways, e.g. use file counter and when it is 0 (all files loaded) you will execute step2() else you will ignore it and just decrease counter.
p.s. the Raja Jaganathan way will work as well, only it is queued loading whereas with the counter you can start loading of all files at the beginning.

Object Data Type in AS3 and Flash Builder 4.5.1

I am trying to save a Sprite object as a file on the device I'm working on and it seems to work. the problem I'm having is reading the saved file back and placing it back on stage as a sprite. Below is the code I have so far, could someone tell me what it is I'm doing wrong? I have a suspicion that the saved isn't what I expect it to be since the file sizes have been under a kilobyte.
public function save_album(e:Event):void
{
var outFile:File = File.documentsDirectory; // dest folder is desktop
outFile = outFile.resolvePath("canvas3.bin");
var fs:FileStream = new FileStream();
var bytes:ByteArray = new ByteArray();
//trace (File.documentsDirectory.url + "/canvas2.bin");
fs.open(outFile, FileMode.WRITE);
bytes.writeObject(graffitiContainer) //graffitiContainer is a Sprite
bytes.position = 0;
fs.writeBytes(bytes, 0, bytes.length);
fs.close();
}
public function open_album(e:Event):void
{
var inBytes:ByteArray = new ByteArray();
var inFile:File = File.documentsDirectory;
inFile = inFile.resolvePath("canvas3.bin"); // name of file to read
var inStream:FileStream = new FileStream();
inStream.open(inFile, FileMode.READ);
inStream.readBytes(inBytes, 0, inBytes.length);
inStream.close();
inBytes.position = 0;
ui.removeChild(graffitiContainer);
var obj:Sprite = inBytes.readObject() as Sprite; //returns a null
graffitiContainer = obj;
ui = new UIComponent();
graffitiContainer.x = 0;
graffitiContainer.y = 100;
ui.addChild(graffitiContainer);
}
Not fully sure I understand what you're trying to accomplish; however, this implementation doesn't do what you're thinking - writeObject could only serialize general public properties, and not the graphics member.
You could render it to a Bitmap.
Saw a blog post about this:
http://jacwright.com/201/serializing-display-objects/