Click here to Skip to main content
6,291,124 members and growing! (15,558 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.NET 2.0, WinXP, Vista, ADO.NET, VS2005, Dev
Posted:20 Feb 2007
Updated:16 May 2008
Views:66,015
Bookmarked:52 times
Unedited contribution
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
13 votes for this article.
Popularity: 4.71 Rating: 4.23 out of 5
2 votes, 15.4%
1

2
1 vote, 7.7%
3
3 votes, 23.1%
4
7 votes, 53.8%
5
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


Member
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
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 26 (Total in Forum: 26) (Refresh)FirstPrevNext
GeneralError while deleting archived files manually PinmemberGISDevil13:11 21 Apr '08  
GeneralRe: Error while deleting archived files manually Pinmemberdmihailescu14:02 24 Apr '08  
GeneralRe: Error while deleting archived files manually PinmemberGISDevil6:27 28 Apr '08  
GeneralRe: Error while deleting archived files manually Pinmemberdmihailescu13:59 28 Apr '08  
GeneralRe: Error while deleting archived files manually PinmemberGISDevil9:17 29 Apr '08  
GeneralRe: Error while deleting archived files manually Pinmemberdmihailescu16:05 29 Apr '08  
QuestionException in ZipFile PinmemberHASEENAA1:51 14 Feb '08  
GeneralRe: Exception in ZipFile Pinmemberdmihailescu11:36 15 Feb '08  
Questionget folder Pinmembermanhnhien20917:16 11 May '07  
GeneralGetfolder Pinmembermanhnhien20917:09 11 May '07  
GeneralMicrosoft.VisualStudio.Zip PinmemberDavid Hay16:59 7 Mar '07  
GeneralRe: Microsoft.VisualStudio.Zip Pinmemberdmihailescu12:02 28 Mar '07  
GeneralRe: Microsoft.VisualStudio.Zip PinmemberAdam Tibi3:54 17 Apr '07  
GeneralAbout Documentation PinmemberHawk77718:00 27 Feb '07  
GeneralArchive Size PinmemberJamsey120014:07 27 Feb '07  
GeneralRe: Archive Size Pinmemberdmihailescu5:00 28 Feb '07  
GeneralRe: Archive Size PinmemberJamsey120015:04 28 Feb '07  
GeneralRe: Archive Size PinmemberAleksei Krassovskikh10:25 17 May '08  
GeneralRe: Archive Size Pinmemberdmihailescu6:55 18 May '08  
GeneralRe: Archive Size PinmemberAleksei Krassovskikh6:44 20 May '08  
GeneralHow to unzip password protected zip files? Pinmembersakthivenkatesh14:06 22 Feb '07  
GeneralRe: How to unzip password protected zip files? Pinmemberdmihailescu4:44 23 Feb '07  
GeneralRe: How to unzip password protected zip files? Pinmembersakthivenkatesh6:58 23 Feb '07  
GeneralGood new bicycle :) PinmemberValeri10:19 21 Feb '07  
GeneralMissing images PinsupporterMark Nischalke6:18 21 Feb '07  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 16 May 2008
Editor:
Copyright 2007 by dmihailescu
Everything else Copyright © CodeProject, 1999-2009
Web20 | Advertise on the Code Project