Simple Application to Zip and UnZip Files in C# Using J# Libraries

Introduction

Recently, I had to build a private online photo album application. I planned to provide the users with the option to upload images as a zip file. I needed this option to be implemented in ASP.Net 1.1 because my hosting service provider didn’t have .NET 2.0 support. An online search led me to a blog with this wonderful idea of using the zip option in J# and it really attracted me. I did want to try it out and the results were pretty cool. Even though this sample application was written in .NET 2.0, the .NET framework 2.0 has libraries to work with compressing and decompressing files. I will soon come up with an article to illustrate the use of .NET 2.0 libraries for compressing and decompressing functionalities. In this article, I will explain the usage of zip functionality in J# from C# code. The code in this application has been designed to be reused in a copy/paste fashion and not as a library.

Background

This application consumes J# classes internally. For this, you first must refer to the J#.NET library. Physically, it resides as a file named vjslib.dll. If you are not sure how to refer to a library in your project, please follow these steps:

  1. Right-click your project in Server Explorer and click “Add Reference.”
  2. Select the .NET tab.
  3. Scroll down and select “vjslib”.
  4. Click OK and you are there.

Now, you can refer the Java library classes within your application. In fact, this was the first time I tried to refer to the J# classes and, personally, it was a moment I would never forget in my programming life. I just may be thrilled about the usage of the whole power and options of Java language within my C# programs (if the need arises).

Import the following namespaces for ease of coding.


java.util;
java.util.zip;
java.io;

The java.util.zip namespace contains the classes and methods to implement the compress and decompress functionalities within your code. The main classes used from the above namespaces are these:


  • ZipFile

  • ZipEntry

  • ZipOutputSteam

  • Enumeration

Programmatically, a ZipFile object can be considered equivalent to a physical zip file. A ZipFile can contain multiple ZipEntry objects apart from the actual content of the zipped files. In fact, each ZipEntry object is the metadata about a zip file. The ZipOutputStream class represents a writable stream pointing to a zip file. This stream can be used to write ZipEntry objects and content to the zip file. Enumeration enables iteration through each element in a collection.

Using the Code

Following is a code listing to create a zip file.

private void Zip(string zipFileName, string[] sourceFile)
{
   FileOutputStream filOpStrm = new FileOutputStream(zipFileName);
   ZipOutputStream zipOpStrm  = new ZipOutputStream(filOpStrm);
   FileInputStream filIpStrm  = null;
   foreach(string strFilName  in sourceFile)
   {
      filIpStrm = new FileInputStream(strFilName);
      ZipEntry ze = new  ZipEntry(Path.GetFileName(strFilName));
                                  zipOpStrm.putNextEntry(ze);
      sbyte[] buffer =  new
      sbyte[1024];
      int len =  0;
      while ((len = filIpStrm.read(buffer))  > = 0)
      {
         zipOpStrm.write(buffer, 0, len);
      }
   }
   zipOpStrm.closeEntry();
   filIpStrm.close();
   zipOpStrm.close();
   filOpStrm.close();
}

The above Zip() method accepts two parameters:


  1. zipFileName: Zip file name including the path and

  2. sourceFile: String array of file names that are to be zipped.

The FileOutputStream class is capable of writing content to a file. Its constructor accepts the path of the file to which you want to write. The FileOutputStream object is then supplied to an instance of the ZipOutputStream class as a parameter. The ZipOutputStream class represents a writable stream to a zip file.

The foreach loops through each file to be zipped, creates corresponding zip entries, and adds them to the final zip file. Taking a deeper look into the code, a FileInputStream object is created for each file to be zipped. The FileInputStream object is capable of reading from a file as a stream. Then a ZipEntry object is created for each file to be zipped. The constructor of the ZipEntry class accepts the name of the file. Path.GetFileName() returns the file name and extension of the specified path string.

The newly created ZipEntry object is added to the ZipOutputStream object by using its putNextEntry() method. In fact, a ZipEntry merely represents metadata of the file entry. You still need to add actual content into the zip file. Therefore, you need to transfer data from the source FileInputStream to destination FileOutputStream. This is exactly what the while loop does in the piece of code above. It reads content from the source file and writes it into the output zip file. Finally, the closeEntry() method of the ZipOutputStream class is called and this causes the physical creation of the zip file. All the other streams created also are closed.

private void Extract(string zipFileName, string destinationPath)
{
ZipFile zipfile = new ZipFile(zipFileName);
List<ZipEntry> zipFiles = GetZippedFiles(zipfile);

foreach (ZipEntry zipFile in zipFiles)
{
   if (!zipFile.isDirectory())
   {
      InputStream s = zipfile.getInputStream(zipFile);
      try
      {
         Directory.CreateDirectory(destinationPath + "\\"  +
            Path.GetDirectoryName(zipFile.getName()));
         FileOutputStream dest = new
            FileOutputStream(Path.Combine(destinationPath + "\\" +
            Path.GetDirectoryName(zipFile.getName()),
            Path.GetFileName(zipFile.getName())));
         try
         {
            int len = 0;
            sbyte[] buffer = new  sbyte[7168];
            while ((len = s.read(buffer))  > = 0)
            {
               dest.write(buffer, 0, len);
            }
         }
         finally
         {
            dest.close();
         }
      }
      finally
      {
         s.close();
      }
   }
}
}

The ExtractZipFile() method accepts two parameters: the zip file name(including path) to be extracted and the destination path where the files are to be extracted. It then creates a ZipFile object and retrieves entries in the zip file using GetZipFiles() method. This method will be discussed later in this article. The foreach loop iterates through all the entries in the zip file and, in each iteration, the entry is extracted to the specified folder. The code in the foreach loop executes only if the entry is not a folder. This condition is verified by using the isDirectory() method of the ZipEntry object. Each entry is read into an InputStream using the getInputStream() method of the ZipFile object. This InputStream acts as the source stream. The destination stream is a FileOutputStream object that is created based on the specified destination folder.

Here, you use the getName() method of the ZipEntry object to get the file name (including the path) of the entry. During the extraction, the original folder structure is maintained. In the while loop that follows, content from the source InputStream is written to the destination FileOutputStream. The source stream is read to a buffer using the read() method. It reads 7 Kb in a sequence into a temporary buffer and then the write() method of the destination FileOutputStream writes the content to the stream from the buffer, using the write method. It is in the finally block that follows that the destination FileOutputStream is closed and the content is physically written to disk.

private List<ZipEntry> GetZipFiles(ZipFile zipfil)
{
   List<ZipEntry> lstZip = new List<ZipEntry>();
   Enumeration zipEnum = zipfil.entries();
   while(zipEnum.hasMoreElements())
   {
      ZipEntry zip = (ZipEntry)zipEnum.nextElement();
      lstZip.Add(zip);
   }
   return lstZip;
}

The GetZipFiles() method returns a generic List of ZipEntry objects taking a ZipFile object as an argument. The method creates a generic collection of a ZipEntry type. Now comes the use of an interesting feature in the Java language: the use of Enumeration. Note that it’s not the enum type that you have in C#. An object that implements the Enumeration interface generates a series of elements, one at a time. Successive calls to the nextElement() method return successive elements of the series. The hasMoreElements() method returns a boolean value indicating whether the Enumerator contains more elements. Here, the entries() method of the ZipFile class returns an Enumeration of ZipEntry objects. The code then iterates through the Enumeration and populates the List. Finally, the populated List is returned.

Apart from the above listed code, the downloadable source code for the sample application contains some extra code to handle the UI part of the application; in other words, entries made to the ListBox and handling the progress bar. I haven’t included them in this article because I didn’t want to lose focus from the main objective of the article. The code is comprehensive, but the UI controls can be handled in better ways, keeping performance and usability in mind. One good option might be to keep your zip functionality separate from the UI thread, so that interactivity is well maintained.

Points of Interest

One interesting fact to note while using the Java library is the difference in naming conventions, especially the naming of methods. For C# coders, methods give the feel of variable names. Also, the way the classes are named is a distinguishable factor.

Even though you can’t use the features of Java beyond an extent because the runtime decides the main advantages of a platform, you take advantage of using the the Java libraries. The gives an upper hand over Java for C#. I wish that Microsoft will continue supporting J#.

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read