This application consumes J# classes internally. For this we
must first refer to the J# .NET library. Physically it resides as a file named
vjslib.dll. If you are not very sure how to refer to a library in your project
please follow the below steps:
Right click your project in Server Explorer and click on
"Add Reference" -> Select the .NET tab -> Scroll down and
select "vjslib" -> 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 am trying to refer to the J# classes and personally it was a moment I
would never forget in my programming life. It made me thrilled about the usage
of the whole power and options of the Java language within my C# programs (if
need arises).
Import the following namespaces for ease of coding.
Listing 1 - Directives
using java.util;
using java.util.zip;
using java.io;
The java.util.zip namespace contains the classes and methods
to implement the compress and uncompress functionalities within our code. The
main classes used from the above namespaces are
·
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.
Below is a code listing to create a ZIP file.
Listing 2 – 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,
zipFileName - ZIP file name including the path and
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 we wish to write.
FileOutputStream object is then supplied to an instance of 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 each 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 using its putNextEntry() method. In fact, a ZipEntry
merely represents metadata of a file entry. You still need to add the actual
contents into the ZIP file. Therefore you need to transfer data from source
FileInputStream to destination FileOutputStream. This is exactly what the while
loop does in the above piece of code. 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 are also closed.
Next we'll see see how to reverse the process and unzip a
file.
Listing 3 – Extract a ZIP file
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; ZIP file
name (including path) to be extracted and 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 afterwards
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 using the isDirectory() method of the ZipEntry object.
Each entry is read into an InputStream using getInputStream() method of ZipFile
object. This InputStream acts as the source stream. The destination stream is a
FileOutputStream object which is created based on the specified destination
folder. Here we use the getName() method of ZipEntry object to get the file
name (including path) of the entry. During the extraction, the original folder
structure is maintained. In the while loop that follows, contents from the
source InputStream are written to the destination FileOutputStream. The source
stream is read to a buffer using the read() method. It reads 7Kb in a sequence
into a temporary buffer and the write() method of destination FileOutputStream
writes the content to the stream from the buffer, using the write method. In
the finally block that follows, the destination FileOutputStream is closed and
the content is physically written to disk.
Listing 4 – Get the contents of a ZIP file
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 argument. The method creates a generic
collection of 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 we
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 if 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. Something similar exists in many .NET
collections that implement the IEnumerable and IEnumerator interfaces.
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, ie., 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 the focus
of 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 our zip functionality separate from the
UI thread, so that interactivity is maintained.