5,427,303 members and growing! (15,147 online)
Email Password   helpLost your password?
General Programming » Algorithms & Recipes » General     Intermediate License: The Code Project Open License (CPOL)

Zip/Unzip using java.util.zip .net namespace and more

By dmihailescu

Zip/Unzip using java.util.zip .net from managed code
C# 2.0, C#Windows, .NET, .NET 2.0, WinXP, Vista, ADO.NET, VS2005, Visual Studio, Dev

Posted: 20 Feb 2007
Updated: 16 May 2008
Views: 46,768
Bookmarked: 48 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
13 votes for this Article.
Popularity: 4.71 Rating: 4.23 out of 5
2 votes, 15.4%
1
0 votes, 0.0%
2
1 vote, 7.7%
3
3 votes, 23.1%
4
7 votes, 53.8%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article
Screenshot - zipstrip2.png

Introduction

When dealing with zip files you have a few choices: use native APIs from third party Dlls, java APIs or .Net APIs.
If you rush to use APIs from System.IO.Compress .net namespace you will be very disappointed.
For reasons only Microsoft knows, the support is limited to streams only and lacks completely
for multi-files archives. This was probably a reason why third party .net libraries like SharpZipLib cropped up.
If you don't trust the free software, you might be surprised to find out that you can find .net support
for multi-file archives in .net buried in J# assemblies that offers parity with Java APIs.
To make a useful application that uses it I started with an existing code project application that is very handy
when backing up source code. I replaced the SharpZipLib references and used the Microsoft's J# APIs instead.
When porting the application I noticed that SharpZipLib API's were looking very similar with J# APIs and that
made my work so much easier.
To make this utility more enticing to use I've added quite a few features that I will detail below.

Background

In order to use Microsoft's API for multi-file zips and Java streams, you have to add vjslib.dll and vjslibcw.dll
.net assemblies as project references. They are part of J# distribution pack.
The Java like types will show up in the java.util.zip namespace. Since Microsoft's documentation on this
topic is quite Spartan ,I often had to rely on intellisense to figure it out.
For simplicity's sake, some nonessential UI code is omitted bellow and can be found only in the source code provided.

Listing the content of a zip file

Below you could see a snippet of code edited for simplicity that enumerates the files in the archive:

public static List<string > GetZipFileNames(string zipFile)
{
    ZipFile zf = null;
    List<string > list = new List<string >();
    try
    {
        zf = new ZipFile(zipFile);
        java.util.Enumeration enu = zf.entries();
        while (enu.hasMoreElements())
        {
            ZipEntry zen = enu.nextElement() as ZipEntry;
            if (zen.isDirectory())
                continue;//ignore directories
            list.Add(zen.getName());
        }

    }
    catch(Exception ex) 
    {
        throw new ApplicationException("Please drag/drop only valid zip files\nthat are not password protected.",ex);
    }
    finally
    {
        if (zf != null)
            zf.close();               
    }
    return list;
}

As you probably noticed ZipEntry and ZipFile are easy to use for this goal.

Zipping files from a folder

Below you could see a helper method used to zip the files from a folder:

private static void _CreateZipFromFolder(string Folder, IsFileStrippableDelegate IsStrip)
{
    System.IO.DirectoryInfo dirInfo =
        new System.IO.DirectoryInfo(Folder);
    System.IO.FileInfo[] files = dirInfo.GetFiles("*");//all files
    foreach (FileInfo file in files)
    {
        if (IsStrip != null && IsStrip(file.FullName))
            continue;//skip, don't zip it
        java.io.FileInputStream instream = new java.io.FileInputStream(file.FullName);
        int bytes = 0;
        string strEntry = file.FullName.Substring(m_trimIndex);
        _zos.putNextEntry(new ZipEntry(strEntry));
        while ((bytes = instream.read(_buffer, 0, _buffer.Length)) > 0)
        {
            _zos.write(_buffer, 0, bytes);
        }
        _zos.closeEntry();
        instream.close();
    }

    System.IO.DirectoryInfo[] folders = null;
    folders = dirInfo.GetDirectories("*");
    if (folders != null)
    {
        foreach (System.IO.DirectoryInfo folder in folders)
        {
            _CreateZipFromFolder(folder.FullName, IsStrip);
        }
    }
}

The IsStrip delegate acts as a filter that trashes the unwanted files.

Unzipping files from a zip file

Below you could see an edited for brevity piece of code used to unzip the files from a zip:

ZipInputStream zis = null;
zis = new ZipInputStream(new java.io.FileInputStream(file));
ZipEntry ze = null;
while ((ze = zis.getNextEntry()) != null)
{
    if (ze.isDirectory())
        continue;//ignore directories
    string fname = ze.getName();
    bool bstrip = IsStrip != null && IsStrip(fname);
    if (!bstrip)
    {
        //unzip entry
        int bytes = 0;
        FileStream filestream = null;
        BinaryWriter w = null;
        string filePath = Folder + @"\" + fname;
        if(!Directory.Exists(Path.GetDirectoryName(filePath)))
            Directory.CreateDirectory(Path.GetDirectoryName(filePath));
        filestream = new FileStream(filePath, FileMode.Create);
        w = new BinaryWriter(filestream);
        while ((bytes = zis.read(_buffer, 0, _buffer.Length)) > 0)
        {
            for (int i = 0; i < bytes; i++)
            {
                unchecked
                {
                    w.Write((byte)_buffer[i]);
                }
            }
        }
    }
    zis.closeEntry();
    w.Close();
    filestream.Close();

    }
    if (zis != null)
        zis.close();
}

Again the IsStrip delegate acts as a filter that trashes the unwanted files.
Also I had to mix the java.io with System.IO namespaces because of the sbyte[] array.

Changing the zip file

You can not directly modify a zip file. However you can create another zip and copy only select files in it.
When the transfer is complete we can rename the new file as the original and it would look like as we
changed the zip. The edited for brevity method below receives a list of string with the unwanted files:

public static void StripZip(string zipFile, List<string > trashFiles)
{
    ZipOutputStream zos = null;
    ZipInputStream zis = null;
    //remove 'zip' extension
    bool bsuccess = true;
    string strNewFile = zipFile.Remove(zipFile.Length - 3, 3) + "tmp";
    zos = new ZipOutputStream(new java.io.FileOutputStream(strNewFile));
    zis = new ZipInputStream(new java.io.FileInputStream(zipFile));
    ZipEntry ze = null;
    while ((ze = zis.getNextEntry()) != null)
    {
        if (ze.isDirectory())
            continue;//ignore directories
        string fname = ze.getName();
        bool bstrip = trashFiles.Contains(fname);
        if (!bstrip)
        {
            //copy the entry from zis to zos
            int bytes = 0;
            //deal with password protected files
            zos.putNextEntry(new ZipEntry(fname));
            while ((bytes = zis.read(_buffer, 0, _buffer.Length)) > 0)
            {
                zos.write(_buffer, 0, bytes);
            }
            zis.closeEntry();
            zos.closeEntry();
        }
    }
    if (zis != null)
        zis.close();
    if (zos != null)
        zos.close();
    if (bsuccess)
    {
        System.IO.File.Delete(zipFile + ".old");
        System.IO.File.Move(zipFile, zipFile + ".old");
        System.IO.File.Move(strNewFile, zipFile);
    }
    else
        System.IO.File.Delete(strNewFile);
}

Improvements over the version 1.0

To make this tool more attractive I've added some improvements of my own: The first one to notice
is the usage of a checked list box that allows doing manual changes on the fly.
My favorite is the ability to edit the list of filter extensions that are bound to the CPZipStripper.exe.xml
file through a DataTable. Here is an edited snapshot of this file.

<configuration>
  <maskRow maskField="*.plg" / >
  <maskRow maskField=".opt" / >
  <maskRow maskField=".ncb" / >
  <maskRow maskField=".suo" / >
  <maskRow maskField="*.pdb" / >
......
</configuration>

Reading data from the config file

Notice that in the application configuration file we keep not only the appSetttings node,
but also the files, paths, and most importantly the DataTable content.
Loading the data from this xml file in the respective lists and dataSet is easy:

XmlDocument xd = new XmlDocument();
xd.Load(cfgxmlpath);
//use plain xml xpath for the rest
m_paths.Clear();
XmlNode xnpath = xd["configuration"]["paths"];
if(xnpath!=null)
{
    foreach(XmlNode xn in xnpath.ChildNodes)
    {
        m_paths.Add(xn.InnerXml);                        
    }    
}
XmlNode xnfile = xd["configuration"]["files"];
if(xnfile!=null)
{
    foreach(XmlNode xn in xnfile.ChildNodes)
    {
        m_files.Add(xn.InnerXml);                        
    }    
}
//use the data set
m_extensions.Clear();
_dataSet = new DataSet("configuration");
DataTable mytable = new DataTable("maskRow");
DataColumn exColumn = new DataColumn("maskField", 
    Type.GetType("System.String"), null, MappingType.Attribute);
mytable.Columns.Add(exColumn);
_dataSet.Tables.Add(mytable);
_dataSet.Tables[0].ReadXml(MainForm.cfgxmlpath);
for (int i = 0; i < _dataSet.Tables[0].Rows.Count; i++)
{
    DataRow row = _dataSet.Tables[0].Rows[i];
    string val = row[0].ToString().ToLower();
    if (val.Length > 0)//no empty mask
    {
    //.....code eliminated for brevity
    }
    else
    {   //don't show empty rows
        row.Delete();
    }
}
_dataSet.Tables[0].AcceptChanges();

Writing data into the config file

Using WriteXml from the dataSet will eliminate the data that does not belong to the table.
For this reason we have to save it before calling WriteXml and restore it afterwards:

XmlDocument xd = new XmlDocument();
//get the original
xd.Load(MainForm.cfgxmlpath);
//save nodes not part of the dataset
XmlNode xnpath = xd["configuration"]["paths"];
XmlNode xnfile = xd["configuration"]["files"];
XmlNode lastFolderpath = xd["configuration"]["LastUsedFolder"];     

//write the masks
_dataSet.WriteXml(MainForm.cfgxmlpath);

//restore the old saved nodes
xd.Load(MainForm.cfgxmlpath);
if(xnpath != null)
    xd.DocumentElement.AppendChild(xnpath);
if (xnfile != null)
     xd.DocumentElement.AppendChild(xnfile);
if (lastFolderpath != null)
    xd.DocumentElement.AppendChild(lastFolderpath);
xd.Save(MainForm.cfgxmlpath);

Using the wild chars for the filter

I'm not going to get into details on this one, but as you've already noticed in the xml snippet,
you can use * and ? chars.
It's a good ideea that the first thing you do when you open this application is setting the configuration.

Screenshot - config.png

Explorer folder context menu

I've added some new functionality in regards to using the context menu from Explorer.
You should start the exe only once before you can right click on the folder and zip it.

Screenshot - Menu.png

History

As a utility I consider version 2.x to be an improvement over the old one.
You can use it to some extent as a Winzip replacement, but it lacks features like encryption.
.Net 2.0 and J# package have to be installed on your machine to run it. If you have problems running
the exe alone,it might be because you are missing the J# distribution package or the .Net 2.0 runtime.
In that that's the case, I recommend you try to install this msi install file I've created or download
vjredist_32bit.zip and install it from locally.

Version 2.3

I've added email capability to the application using the Outlook wrapper and made other small enhancements.
I also included the deployment project that created the msi file.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

dmihailescu


Decebal Mihailescu is a software engineer with interest in .Net, C# and C++.
Occupation: Web Developer
Location: United States United States

Other popular Algorithms & Recipes articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 25 of 26 (Total in Forum: 26) (Refresh)FirstPrevNext
Subject  Author Date 
GeneralError while deleting archived files manuallymemberGISDevil13:11 21 Apr '08  
GeneralRe: Error while deleting archived files manuallymemberdmihailescu14:02 24 Apr '08  
GeneralRe: Error while deleting archived files manuallymemberGISDevil6:27 28 Apr '08  
GeneralRe: Error while deleting archived files manuallymemberdmihailescu13:59 28 Apr '08  
GeneralRe: Error while deleting archived files manuallymemberGISDevil9:17 29 Apr '08  
GeneralRe: Error while deleting archived files manuallymemberdmihailescu16:05 29 Apr '08  
QuestionException in ZipFilememberHASEENAA1:51 14 Feb '08  
GeneralRe: Exception in ZipFilememberdmihailescu11:36 15 Feb '08  
Questionget foldermembermanhnhien20917:16 11 May '07  
GeneralGetfoldermembermanhnhien20917:09 11 May '07  
GeneralMicrosoft.VisualStudio.ZipmemberDavid Hay16:59 7 Mar '07  
GeneralRe: Microsoft.VisualStudio.Zipmemberdmihailescu12:02 28 Mar '07  
GeneralRe: Microsoft.VisualStudio.ZipmemberAdam Tibi3:54 17 Apr '07  
GeneralAbout DocumentationmemberHawk77718:00 27 Feb '07  
GeneralArchive SizememberJamsey120014:07 27 Feb '07  
GeneralRe: Archive Sizememberdmihailescu5:00 28 Feb '07  
GeneralRe: Archive SizememberJamsey120015:04 28 Feb '07  
GeneralRe: Archive SizememberAleksei Krassovskikh10:25 17 May '08  
GeneralRe: Archive Sizememberdmihailescu6:55 18 May '08  
GeneralRe: Archive SizememberAleksei Krassovskikh6:44 20 May '08  
GeneralHow to unzip password protected zip files?membersakthivenkatesh14:06 22 Feb '07  
GeneralRe: How to unzip password protected zip files?memberdmihailescu4:44 23 Feb '07  
GeneralRe: How to unzip password protected zip files?membersakthivenkatesh6:58 23 Feb '07  
GeneralGood new bicycle :)memberValeri10:19 21 Feb '07  
GeneralMissing imagessupporterMark Nischalke<