AS3 port of JZLib

Last month I was working on a project that used FZip to decompress some zip files in flash.

One tricky thing about FZip is that when running in Flash Player it requires your zip files to have an adler32 checksum for each file in order to work. This is normally fixed with a work around python script provided with FZip.

The python script is easy and all, but why not figure out how to do it in pure AS3 with more standard zip files?

The checksum is needed because AS3’s ByteArray only supports ZLib when running in Flash Player. In AIR it supports deflate which is what zip files use by default. I would be really curious to hear from Adobe why they chose not to support deflate since you need deflate for Zlib to work anyway.

I decided to implement inflate in as3 but I didn’t want to do it with new code so I looked for FOSS projects to port. JZlib was a good choice because Java is similar to AS3 and it didn’t rely on any external system calls.

This port supports everything in JZlib so you can use it for any inflate or deflate operations you might need.

To use with FZip

I used this library in FZip so it no longer requires that zip files be converted before use. It is tested to be working with the OSX cli zip command. It doesn’t work with OSX finder zip compression because of another issue.

In FZipFile.as:

protected function parseContent(data:IDataInput):void {
	if(_compressionMethod === COMPRESSION_DEFLATED && !_encrypted) {
		if(HAS_INFLATE) {
			// Adobe Air supports inflate decompression.
			// If we got here, this is an Air application
			// and we can decompress without using the Adler32 hack
			// so we just write out the raw deflate compressed file
			data.readBytes(_content, 0, _sizeCompressed);
		} else if(_hasAdler32) {
			// Add zlib header
			// CMF (compression method and info)
			_content.writeByte(0x78);
			// FLG (compression level, preset dict, checkbits)
			var flg:uint = (~_deflateSpeedOption << 6) & 0xc0;
			flg += 31 - (((0x78 << 8) | flg) % 31);
			_content.writeByte(flg);
			// Add raw deflate-compressed file
			data.readBytes(_content, 2, _sizeCompressed);
			// Add adler32 checksum
			_content.position = _content.length;
			_content.writeUnsignedInt(_adler32);
		} else {
			data.readBytes(_content, 0, _sizeCompressed);
 
			//throw new Error("Adler32 checksum not found.");
		}
		isCompressed = true;
	} else if(_compressionMethod == COMPRESSION_NONE) {
		data.readBytes(_content, 0, _sizeCompressed);
		isCompressed = false;
	} else {
		throw new Error("Compression method " + _compressionMethod + " is not supported.");
	}
	_content.position = 0;
}
 
protected function uncompress():void {
	if(isCompressed && _content.length > 0) {
		_content.position = 0;
		if(HAS_INFLATE) {
			_content.uncompress.apply(_content, ["deflate"]);
		} else if(_hasAdler32) {
			_content.uncompress();
		} else {
			var uncompr:ByteArray = new ByteArray();
			var d_stream:ZStream=new ZStream();
			d_stream.next_in = _content;
			d_stream.next_in_index = 0;
			d_stream.next_out = uncompr;
			d_stream.next_out_index = 0;
			
			var err:int = d_stream.inflateInitWithNoWrap(true);
			while(d_stream.total_in <= _content.length && i<=_content.length*4) {
				d_stream.avail_in=d_stream.avail_out=10;
			
				err=d_stream.inflate(JZlib.Z_NO_FLUSH);
				if(err == JZlib.Z_STREAM_END) {
					System.println("decompress success:" + this.filename);
					break;
				} else if (err == JZlib.Z_STREAM_ERROR) {
					System.println("stream error:" + this.filename + " " + d_stream.msg);
					break;
				} else if (err == JZlib.Z_DATA_ERROR) {
					System.println("data error:" + this.filename + " " + d_stream.msg);
					break;
				//} else {
				//	System.println("status:" + this.filename + " " + err);
				}				
			}
			err=d_stream.inflateEnd();
 
			_content = uncompr;			
		}
		_content.position = 0;
		isCompressed = false;
	}
}

*** UPDATE 2/9/2010: Thanks to Kathrin Furtlehner for sending me a test case for a bug where it wasn’t extracting the full file when dealing with longer strings. I updated the patched FZip archive to reflect the fix.

  • stan

    Hi Tea,

    I would really like to use your Fzip class, but unfortunatelly the source files use a class that I can’t find in the source…: com.wirelust.util.Cast;
    could you please add it to the repository, or somewhere so I can check the usage of your class?

    Thanks,
    Stan

  • Tea

    Oops. sorry I forgot to check that in. You can get it here:
    http://code.google.com/p/as3zlib/source/browse/trunk/src/com/wirelust/util/Cast.as

    I’ll update the zip archive later on.

  • Ben

    I made some modifications to this library to fix some flex3 compilation warnings as well as some corruption that occurs when using Z_SYNC_FLUSH. I attempted to send you an email but have no idea if it was the correct address, let me know if you would like me to send you a diff, and thanks!

  • damned

    Hey Tea,

    I love this library, it works like a charm.. well zipping up data does.. when you use

    var zip:FZip = new Zip();

    // load a file
    // add listeners

    var file:FZipFile = zip.getFileAt(FILE_INDEX);

    Does matter if its an image or in my case I tried \utf-8\ text.. It chops off alittle of the end.. Why is that?? Thanks

  • Are you sure this really works ?
    I am trying to build a swc in flex containing this code, but i get language errors

    – the \in\ variables, in as3, \in\ – is keyword
    – the throws at the end of each method, in java this is possible, in as3 i doubt

    Please, make it work

  • how to use the fzip-prepare.py? pls reaply me via this email

  • Thierry

    Hello,
    I use your classe, it works well, but for texts files (in my case xml files) the getContentAsString cut the end of the string….
    If do you plan to fix it, it would be very kind of you !!
    Thanks
    Thierry

  • Tea

    Please email me an example project where this is happening and I will look into fixing it. Unfortunately I don’t have time to investigate without knowing the exact nature of the problem.

  • Tea

    @Thierry: this issue has been tracked down and is fixed in the latest copy of the zip.

  • shaning

    Using the code above, the error: Adler32 checksum not found. was gone. However, I got another error at

    protected function uncompress():void {
    if(isCompressed && _content.length > 0) {
    _content.position = 0;
    if(HAS_INFLATE) {
    _content.uncompress.apply(_content, [deflate]);
    } else {
    _content.uncompress(); // Error at Line 562
    }
    _content.position = 0;
    isCompressed = false;
    }
    }

    Anyone can help? Thanks.
    __________________________

    Error: Error #2058: There was an error decompressing the data.
    at flash.utils::ByteArray/_uncompress()
    at flash.utils::ByteArray/uncompress()
    at deng.fzip::FZipFile/uncompress()[C:Documents and Settingsshaning.yuMy DocumentsFlex Builder 3myTestsrcdeng zipFZipFile.as:562]
    at deng.fzip::FZipFile/get content()[C:Documents and Settingsshaning.yuMy DocumentsFlex Builder 3myTestsrcdeng zipFZipFile.as:144]
    at FileZip/onEnterFrame()[C:Documents and Settingsshaning.yuMy DocumentsFlex Builder 3myTestsrcFileZip.mxml:61]

  • shaning

    where and how can I get ZStream?

  • Tea

    ZStream is in the as3zlib zip. you need to download that and merge it with the modified fzip code,

  • shaning

    Downloaded. Could you kind provide How to do for the downloaded as3zlib zip? Thanks.

  • Tea

    The two zips can be combined into a single src folder. sorry I don’t have time to provide much support beyond this.

  • shaning

    Got how to do already.

  • shaning

    New problem if anyone can help:
    Can’t find com.wirelust.util.Cast
    In the downloaded zip, there is no com/wirelust/util
    If it is a simple as file or a a bundle of files, could you email me at mrgis08@yahoo.com? Thanks.

  • shaning

    Got it already. Never mind. Thanks.

  • Tea

    I updated the link to the zip with the latest copy from subversion. Cast should be in there. as3zlib_snapshot_20100303.zip

  • shaning

    Got an error at
    while(d_stream.total_in <= _content.length && i<=_content.length*4)

    where i is undefined. I guess var i:int should defined somewhere in the source code. Could someone help for this issue? Thanks.

  • shaning

    I tried to unzip a folder; states.zip, which contains 5 dozens of txt file.

    public function FZipTest() {
    zip = new FZip();
    zip.addEventListener(Event.OPEN, onOpen);
    zip.addEventListener(Event.COMPLETE, onComplete);
    zip.load(new URLRequest(\states.zip\)); // in the same directory of this file’s swf
    }

    However, I got such an error:

    Error #2044: Unhandled IOErrorEvent:. text=Error #2124: Loaded file is an unknown type.
    Can anyone hint me where the file type should be defined? Thanks.

  • shaning

    I used the source code for
    famfamfam_silk_icons_v013_cli.zip, which is in http://localhost:3000/myTest/flex_bin/ but still get error.

    Error #2044: Unhandled ioError:. text=Error #2032: Stream Error. URL: http://localhost:3000/myTest/flex_bin/famfamfam_silk_icons_v013_cli.zip

    I don’t know what is wrong with me. Wish someone can share his/her experience. Thanks.

  • shaning

    Question: can flash.display.Loader be used to load a textfile in a zip? I got
    Error #2124: Loaded file is an unknown type.

  • shaning

    Is it true for the following statements:
    1) flash.net.URLLoader does not support to load a file in zip.
    2) flash.display.Loader does not support to load a textfile.
    Thanks if you can share your experience.

  • Tea

    I have no idea what you are trying to do since you didn’t post any code that you are having issues with. URLLoader is well documented by adobe and I suggest you look up its capabilities on google.
    Here is an example of uncompressing and loading a text file:

    public class fzip extends Sprite
    {
    private var zip:FZip;

    public function fzip()
    {
    zip = new FZip();
    zip.addEventListener(Event.COMPLETE, onComplete);
    zip.load(new URLRequest(“20100129.zip”));
    }

    private function onComplete(e:Event):void
    {
    trace(“zip complete”);

    var file:FZipFile = zip.getFileAt(0);

    trace(file.getContentAsString(true));
    }
    }

  • shaning

    I used the code, it is helpful. I have one more Q: The file is textfile, how can I load its content into a string? Thanks.