SCOUG OS/2 For You - June 1999
Cup of Java
Zipping Along
If you have ever tried to write code to deal with compressed data, such as that found in zipped files, then you will probably appreciate how easy it is to do this type of processing in Java. The JDK 1.1 includes a package, java.util.zip, which contains a set of classes designed specifically to facilitate reading and writing ZIPfiles. These classes can deal both with the lz compressed data (used by the various pkware zip/unzip programs) as well as the standard Unix variant (gzip). In addition, there are classes which provide iostreaming capabilities for compressing data "on the fly" and passing it in an extended io pipe.
Zip Basics
The basis for handling compressed data in Java is the concept of deflators and inflators - which are represented in the package by their corresponding classes: Deflator and Inflator. These classes encap-sulate objects that are capable of deflating (or inflating) a data into (or from) a data source.
The Deflator class is designed to provide compression of data using a variety of parameters which define the type and extent of the compres-sion. The Inflator class handles data compressed via a Deflator object to un-compress it. These classes perform their functions via a buffer but are also extensible to support streamed data.
The streamed data classes (DeflatorOutputStream and InflatorInputStream) work with Deflator or Inflator to handle data that is streamed to or from any iostream. In general, a program will deal with data at this level rather than directly at the Deflator/Inflator level. The package also provides several classes for dealing with specific types of compressed data:
- ZipInputStream and ZipOutputStream (for pkzipped files)
- GZipInputStream and GZipOutputStream (for gzipped files)
Accessing a Zipped File
The two main classes for accesing ZIPfile data are ZipFile and ZipEntry. You can attach a zipped file to a ZipFile object (via its constructor) and then access the various content files of the zipped file using ZipEntry objects. There are methods for obtaining information about the zipped file contents as well as processing the data in those contents.
Note: all of the examples in this article assume the following import statements:
import java.util.*;
import java.util.zip.*;
import java.io.*;
|
For example, the following code fragment lists the contents of a ZIPfile for the filename passed in the method invocation:
public void showZippedFiles(String filename) {
try {
//create a zipfile object
ZipFile zf = new ZipFile(filename);
//get enumeration of all entries
Enumeration zipEnum = zf.entries();
// loop through all entries
while (zipEnum.hasMoreElements()) {
ZipEntry ze = (ZipEntry) zipEnum.nextElement(); //get next entry
//list the entry name, compressed size and //uncompressed size
System.out.println(ze.getName() + ", " +
ze.getCompressedSize() + ", " +
ze.getSize() );
}
zf.close();
}
catch(Exception x) {
System.out.println(x.toString());
}
}
This method will list the names of all of the files in the zipfile along with their compressed file size and their uncompressed file size. There are also other attributes that can be listed such as the CRC value and timestamp.
Extracting Files from a Zip
The next example shows how to extract a specified file (given by the second parameter) from within a zipped file (given by the first parameter) and write it to a normal file of the same name. The basic logic is to first get the appropriate entry within the ZIPfile and then open an input stream for this entry. An output stream is created and the data is transferred from the (compressed) input stream to the output file:
public void extractFile(String zipfilename, String filename) {
try {
//get zip object
ZipFile zf = new ZipFile(zipfilename);
//get zip entry
ZipEntry ze = zf.getEntry(filename);
//get an input stream for the entry
InputStream ins = zf.getInputStream(ze);
//create output file
FileOutputStream fos = new FileOutputStream(outFile);
//and an output stream
File outFile = new File(filename);
int bread;
//transfer buffer
byte[] bin = new byte[4096];
//loop through reading the zipped file entry and
// write it to the external file
while ( (bread = ins.read(bin, 0, 4096)) > -1) {
fos.write(bin, 0, bread);
}
ins.close();
fos.close();
}
catch (Exception x) {
System.out.println(x.toString());
}
}
If you want to process the entire zipfile, you can use the ZipInputStream against an inputstream from the file. Then use the getNextEntry() in a loop to automatically process the entries within the zipfile sequentially. In this case, the processing for each entry should test to see if the entry is a directory or a file:
public void processZip(String zipfilename) {
try {
ZipInputStream zis = new ZipInputStream(new InputStream(zipfilename));
ZipEntry ze;
//process sequentially
while ( (ze = zis.getNextEntry()) != null) {
... process the entry (eg read loop) ...
}
zis.close();
}
catch (Exception x) {
System.out.println(x.toString());
}
}
Creating a Zip
You can also write to zipfiles and/or create them, using primarily the ZipOutputStream class. This is more complex than reading since you must specify how to put the data into the file (STORED or DEFLATED) and, if DEFLATED, the level of compression.
The following example shows how to write deflated files into a ZIPfile. The first parameter gives the name of the zipfile to create and the second parameter is a string array indicating the files to zip.
public void createZip(String zipfilename, String[] contents) {
try {
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipfilename));
for (int i=0; i
Compressing Data Streams
While the zipped file handling capabilities described above are useful, the general concept of deflated and inflated iostreams (discussed briefly at the beginning of this article) is very powerful especially in client/server applications that need to pass significant volumes of data per transaction. Normally these applications would use TCP/IP sockets to communicate and would then open an iostream between the sockets to transfer data (the sending object would create an output stream and the receiving object an input stream). By simply wrapping the sending output stream with a DeflatorOuputStream the transmitting socket is now automatically compressing its data as it is being sent.
The receiving socket can wrap its input stream with the InflatorInputStream if it wishes to decompress the data (or it can store the compressed data in a file or other object for later decompression). One drawback to this approach is that it works only if the server is always accessed by clients that are using the compression technique; if some clients are not then additional coding is needed to determine which type of stream is being received and handle both cases.
A complete example of this type of coding is available through the SCOUG programming SIG webpage.
Limitations
There are some limitations on the use of these classes with existing zipped files. The ones I have encountered are:
- Encrypted files in a ZIPare not supported (ie if the password command was used when creating the zip)
- Spanned files (across multiple diskettes) are not supported
- If the zipped file was edited (eg using "delete" or "add" commands) it will sometimes not be processed successfully by the Java support
Summary
Java JDK 1.1 provides a useful set of classes for dealing with compressed data, both in external file formats (pkzip and gzip) as well as at the iostream level for interprocess communication.
Next time we will look at Java's exception handling model.
Cup of Java by Terry Warren is a series that started in April 1999. Prior articles are:
The Southern California OS/2 User Group
P.O. Box 26904
Santa Ana, CA 92799-6904, USA
Copyright 1999 the Southern California OS/2 User Group. ALL RIGHTS
RESERVED.
SCOUG, Warp Expo West, and Warpfest are trademarks of the Southern California OS/2 User Group.
OS/2, Workplace Shell, and IBM are registered trademarks of International
Business Machines Corporation.
All other trademarks remain the property of their respective owners.
|