I am currently building an AIR application with JavaScript (jQuery + jQuery UI), one function downloads zipped files and has to extract the contents somewhere to the local filesystem. Since the files may contain subfolders, i didn’t find any working example supporting subfolders in the docs or via google.
I found an Adobe Tutorial for basic Zip files (see Sources), and after reading the zip specs there was just one small change to their code to get it working with subfolders.
The spec says that entries in the zip file which are directories just end with an ‘/’. Skipping them is the whole magic. (see line 48)
Thankfully, the air.FileStream autocreates director ies, so writing the files to the subfolders does not need any change to the code. I added a basepath as parameter to the outFile function, to complete the for me intuitive function unzipFile(sourceFile, targetPath).
Here is the code:
function unzipFile(sourceFile, targetPath)
{
var bytes = new air.ByteArray();
var fileName = new String();
var flNameLength;
var xfldLength;
var offset;
var compSize;
var uncompSize;
var compMethod;
var signature;
var zfile = air.File.applicationStorageDirectory.resolvePath(sourceFile);
var zStream = new air.FileStream();
zStream.open(zfile, air.FileMode.READ);
bytes.endian = air.Endian.LITTLE_ENDIAN;
while (zStream.position < zfile.size)
{
// read fixed metadata portion of local file header
zStream.readBytes(bytes, 0, 30);
bytes.position = 0;
signature = bytes.readInt();
// if no longer reading data files, quit
if (signature != 0x04034b50)
{
break;
}
bytes.position = 8;
compMethod = bytes.readByte(); // store compression method (8 == Deflate)
offset = 0; // stores length of variable portion of metadata
bytes.position = 26; // offset to file name length
flNameLength = bytes.readShort(); // store file name
offset += flNameLength; // add length of file name
bytes.position = 28; // offset to extra field length
xfldLength = bytes.readShort();
offset += xfldLength; // add length of extra field
// read variable length bytes between fixed-length header and compressed file data
zStream.readBytes(bytes, 30, offset);
bytes.position = 30;
fileName = bytes.readUTFBytes(flNameLength); // read file name
if (fileName.substr(fileName.length - 1, 1) != '/')
{
bytes.position = 18;
compSize = bytes.readUnsignedInt(); // store size of compressed portion
bytes.position = 22; // offset to uncompressed size
uncompSize = bytes.readUnsignedInt(); // store uncompressed size
// read compressed file to offset 0 of bytes; for uncompressed files
// the compressed and uncompressed size is the same
zStream.readBytes(bytes, 0, compSize);
if (compMethod == 8 ) // if file is compressed, uncompress
{
bytes.uncompress(air.CompressionAlgorithm.DEFLATE);
}
outFile(targetPath, fileName, bytes); // call outFile() to write out the file
}
}
}
This is the outFile function which writes the uncompressed files to the local file system.
function outFile(baseDir, fileName, data)
{
var outFile = air.File.applicationStorageDirectory;
outFile = outFile.resolvePath(baseDir+air.File.separator+fileName);
var outStream = new air.FileStream();
outStream.open(outFile, air.FileMode.WRITE);
outStream.writeBytes(data, 0, data.length);
outStream.close();
}
Sources: