4

In a Class of its own – the .NET Zip Library

Share on TwitterShare on TumblrSubmit to StumbleUponSave on DeliciousDigg This

The .NET Framework contains several hundreds of classes, yet something is missing: there is no class for compressing and decompressing Zip archives. This omission however can be made up for by using the .NET Zip Library (NZipLib) – it supports Zip as well as GZip format, is free, and comes with C# source code included.

The NZipLib can be downloaded from ic#code. Even though the library itself is distributed under the GPL (GNU General Public License), it may be used in Non-GPL programs thanks to the particular exemption stated on the homepage.

Usage under ASP.NET

The library is compiled as a DLL assembly, and can be found in the downloadable Zip file in the NZipLib\src\NZipLib\ directory. The first step then is to copy it into the bin directory of the Web site.

Now that the component already is registered thanks to XCOPY Deployment, it can be used immediately. To this end, I have written a small ASP.NET page which lists the contents of a Zip archive: the filename, the file size, the compressed size. The source code is contained in the download for today’s article as ListZipContent.aspx:

<% @Page Language= "C#" %>
<% @Import Namespace="System.IO" %>
<% @Import Namespace="System.Text" %>
<% @Import Namespace="NZlib.Zip" %>
<%
ZipInputStream s = new ZipInputStream(File.OpenRead("c:\\test.zip"));
ZipEntry theEntry = null;
StringBuilder strOutput = new StringBuilder();

while ((theEntry = s.GetNextEntry()) != null) 
{
  strOutput.Append("<tr><td>");
  strOutput.Append(theEntry.Name);
  strOutput.Append("</td><td>");
  strOutput.Append(theEntry.Size.ToString());
  strOutput.Append("</td><td>");
  strOutput.Append(theEntry.CompressedSize.ToString());
  strOutput.Append("</td></tr>\r\n");
}

s.Close(); // clean up the file resource immediately
%>

<table>
<tr><th>File name</th><th>Actual Size</th><th>Compressed Size</th></tr>
<%
  Response.Write(strOutput.ToString());
%>
</table>

This example uses the classes of the NZlib.Zip namespace, thus we need a corresponding Import statement. You may refer to a detailed description of all namespaces and classes in the NZipLib\doc\api\Documentation.chm help file. This documentation is similar to the .NET Framework reference, navigation should thus not be a problem.

In the above example, I use the ZipInputStream class to access an already existing (hard coded) Zip file. After opening the archive, I traverse all entries of the ZipEntry class using a while loop and buffer the table content generated in a StringBuilder object.

The output is pretty unspectacular but represents proof that it works as planned:

Viewing Files in Archives

We now have a list of files – but how can we access their content? In other words, how do we decompress the individual files? As for this, a modified example, intended to be run at the command line (ViewZipFile.cs):

using System;
using System.Text;
using System.IO;

using NZlib.Zip;

class MainClass
{
  public static void Main(string[] args)
  {
    ZipInputStream s = new ZipInputStream(File.OpenRead(args[0]));
    ZipEntry theEntry;

    int nBytes = 2048;
    byte[] data = new byte[2048];

    while ((theEntry = s.GetNextEntry()) != null) 
    {
      Console.WriteLine(theEntry.Name);
      Console.Write("Eintrag anzeigen? (j/n)");

      if (Console.ReadLine() == "j") 
      {
        while((nBytes = s.Read(data, 0, data.Length))  > 0)
        {
           Console.Write(new ASCIIEncoding().GetString(data,0,nBytes));
        }
      }
      Console.WriteLine();
    }
    s.Close();
  }
}

Basically, reading a compressed file from the Zip archive is just using the Read command, already well known from handling streams. We process the data obtained so that they are output to the Console encoded as ASCII. Of course, instead of converting, the data might be written to a file on the disk.

Compilation of this application is done as follows (Assumption: NZipLib.dll resides in the same directory as the source code):

csc /target:exe /reference:NZipLib.dll ViewZipFile.cs

It is called like this:

viewzipfile c:\test.zip

And presto, we can view the files in the Zip archive.

Creating Zip Archives

Reading is easy! But what about creating a Zip archive? Well, let us take a look at a small sample program which writes all files in a given subdirectory to a Zip file (CreateZipFile.cs):

using System;
using System.IO;

using NZlib.Zip;

class MainClass
{
  public static void Main(string[] args)
  {
    // all Files in the given Folder will be compressed
    string[] aFilenames = Directory.GetFiles(args[0]);

    // the name of the Zip File is the second Parameter passed in calling
    ZipOutputStream s = new ZipOutputStream(File.Create(args[1]));

    // Set compression level: 0 [none] - 9 [highest]
    s.SetLevel(5); 

    for (int i=0; i < aFilenames.Length; i++)
    {
      FileStream fs = File.OpenRead(aFilenames[i]);

      // normally, the Buffer is allocated once,
      // here we do it once per File for clarity's sake
      byte[] buffer = new byte[fs.Length];
      fs.Read(buffer, 0, buffer.Length);

      // and now we write a ZipEntry & the Data
      ZipEntry entry = new ZipEntry(aFilenames[i]);
      s.PutNextEntry(entry);
      s.Write(buffer, 0, buffer.Length);
    }

    s.Finish();
    s.Close();
  }
}

In the compression scenario, we are working with the ZipOutputStream class, which will be with us when creating the archive. Basically, it is an inversion of the process of unzipping: here we create a ZipEntry, and instead of reading the file from the archive, we write it to the archive.

Usage of the program is as follows:

createzipfile c:\folder c:\zipfile.zip

Personally I think that creating an archive is not at all harder than reading files from an archive.

Conclusion

prajapat