neko.zip: Zip and Tar Gz archives

Note: If you want a cross platform zip solution, you should use the haxe format library (http://lib.haxe.org/p/format). More information about basic usage here: format.zip: Basic usage

Introduction

This tutorial demonstrates how to use zip and tar files in Haxe/Neko. The neko.zip package can be used to read from and write to compressed archives. The important sections in the API are neko.zip.Reader, neko.zip.Writer, and neko.zip.ZipEntry.

Input

The program operates on an existing zip and tar gz file. The program should work on any valid zip and tar gz files, but since the example outputs the files contents to the screen it would be best to create small example files for it to use. Create a directory named "data" in the current directory. Create a text file named "file1.txt" containing "this is a file" in the "data" directory. Then create a text file named "file2.txt" containing "this is another file" also in the "data" directory.

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." Windows users without a "tar" program can run this tutorial by commenting out the three lines of main that calls readTar and outputs its content.

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 will 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 used interchangeably.

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 since its value is true.

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 decompress 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 decompresses 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

neko.zip.Reader must be imported since it defines the neko.zip.ZipEntry type.

import neko.zip.Reader;
import neko.zip.Writer;

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

version #14380, modified 2012-07-08 15:23:02 by cambiata