neko.zip: Zip and Tar Gz archives
Introduction
This tutorial demonstrates how to use zip and tar files in Haxe/Neko. The neko.zip can be used to read and write to compressed archives. The important methods are in neko.zip.Reader, neko.zip.Writer, and neko.zip.ZipEntry.
Input
The program operates on an existing zip and tar gz file. Since the example outputs the file contents to the screen it would be best to create an example file for it to use. Create a directory named "data" in the current directory. In the "data" directory, create a text file named "file1.txt" containing "this is a file." Now create a text file in the "data" directory named "file2.txt" containing "this is another file."
The directory tree should look like this:
data/ file1.txt file2.txt
Use any zip program to create a zip archive of the "data" directory named "data.zip." Also create a tar gz archive of it named "data.tgz."
Pre/Post Conditions
In order to run the example program, you must have "data.zip" and "data.tgz" in the current directory. The program will create a file named "data2.zip" in the same directory. Multiple runs will overwrite "data2.zip."
Walkthrough
In this example we will:
- read a zip file, output its contents
- read a tar gz file, output its contents
- write the contents of the tar gz file to a new zip file
- read the new zip file, output its contents
Code
The code is separated into several functions. We'll go over each in detail.
Main
The main function makes several function calls that basically match the walkthrough. Note that data structures returned from reading zip or tar archives (archiveData in the code below) can be treated the same way.
public static function main() { var archiveData; archiveData = readZip("data.zip"); neko.Lib.println("\nzip files: "); printFiles(archiveData); archiveData = readTar("data.tgz"); neko.Lib.println("\ntar files: "); printFiles(archiveData); neko.Lib.println("\nwriting zip"); writeZip(archiveData, "data2.zip"); neko.Lib.println("done"); neko.Lib.println("\nreading new zip"); archiveData = readZip("data2.zip"); neko.Lib.println("zip files: "); printFiles(archiveData); }
Read Zip
The readZip function opens the zip file, reads it into a data structure, closes it, then returns the data structure.
private static function readZip(fname) { var fin = neko.io.File.read(fname, true); var archiveData = neko.zip.Reader.readZip(fin); fin.close(); return archiveData; }
Read Tar
The readTar is nearly the same as readZip, except that it uses a different method from the neko.zip.Reader class. The second argument of the readTar method indicates that the input file is gzipped.
private static function readTar(fname) { var fin = neko.io.File.read(fname, true); var archiveData = neko.zip.Reader.readTar(fin, true); fin.close(); return archiveData; }
Print Files
This function outputs the file name and content for each entry read from the input file. A file's content is compressed if its compressed field is set to true. We unzip compressed data before outputting it.
private static function printFiles(archiveData : List<neko.zip.ZipEntry>) { for( ii in archiveData ) { var content = (ii.compressed) ? neko.zip.Reader.unzip(ii) : ii.data; neko.Lib.println(" " + ii.fileName + ": " + content); } }
Write Zip
The top section of this function does two things:
- it uncompresses compressed data since the data passed into neko.zip.Writer.writeZip should not already be compressed
- it removes unwanted fields of neko.zip.ZipEntry
The bottom section writes the output file. The third argument of neko.zip.Writer.writeZip is the compression level. A value of -1 means average compression.
private static function writeZip(archiveData : List<neko.zip.ZipEntry>, fname) { // remove unneeded fields of ZipEntry var convert = function(ii:neko.zip.ZipEntry) { var content = (ii.compressed) ? neko.zip.Reader.unzip(ii) : ii.data; return { fileTime:ii.fileTime, fileName:ii.fileName, data:content }; } var dataToWrite = Lambda.map(archiveData, convert); var fout = neko.io.File.write(fname, true); neko.zip.Writer.writeZip(fout, dataToWrite, -1); fout.close(); }
Full Program
Note that neko.zip.Reader must be imported since it defines the neko.zip.ZipEntry type.
import neko.zip.Reader; class ZipEx { public static function main() { var archiveData; archiveData = readZip("data.zip"); neko.Lib.println("\nzip files: "); printFiles(archiveData); archiveData = readTar("data.tgz"); neko.Lib.println("\ntar files: "); printFiles(archiveData); neko.Lib.println("\nwriting zip"); writeZip(archiveData, "data2.zip"); neko.Lib.println("done"); neko.Lib.println("\nreading new zip"); archiveData = readZip("data2.zip"); neko.Lib.println("zip files: "); printFiles(archiveData); } // read zip file private static function readZip(fname) { var fin = neko.io.File.read(fname, true); var archiveData = neko.zip.Reader.readZip(fin); fin.close(); return archiveData; } // read tar gz file private static function readTar(fname) { var fin = neko.io.File.read(fname, true); var archiveData = neko.zip.Reader.readTar(fin, true); fin.close(); return archiveData; } // output data read from archive to screen private static function printFiles(archiveData : List<neko.zip.ZipEntry>) { for( ii in archiveData ) { var content = (ii.compressed) ? neko.zip.Reader.unzip(ii) : ii.data; neko.Lib.println(" " + ii.fileName + ": " + content); } } // write data to zip file private static function writeZip(archiveData : List<neko.zip.ZipEntry>, fname) { // remove unneeded fields of ZipEntry var convert = function(ii:neko.zip.ZipEntry) { var content = (ii.compressed) ? neko.zip.Reader.unzip(ii) : ii.data; return { fileTime:ii.fileTime, fileName:ii.fileName, data:content }; } var dataToWrite = Lambda.map(archiveData, convert); var fout = neko.io.File.write(fname, true); neko.zip.Writer.writeZip(fout, dataToWrite, -1); fout.close(); } }
Compile and run with:
haxe -neko zip.n -main ZipEx.hx neko zip.n
The output should be:
zip files: data/file1.txt: this is a file data/file2.txt: this is another file data/: tar files: data/: data/file1.txt: this is a file data/file2.txt: this is another file writing zip done reading new zip zip files: data/: data/file1.txt: this is a file data/file2.txt: this is another file