3.6 Compressing Files and Directories
Example 3-5 demonstrates an interesting application of
stream classes: compressing files and directories. The classes of
interest in this example are not actually part of the
java.io package, but instead part of the
java.util.zip package. The
Compress class defines two static methods,
gzipFile( ), which compresses a file using GZIP
compression format, and zipDirectory( ), which
compresses the files (but not directories) in a directory using the
ZIP archive and compression format. gzipFile( )
uses the GZIPOutputStream class, while
zipDirectory( ) uses the
ZipOutputStream and ZipEntry
classes, all from java.util.zip.This example demonstrates the versatility of the stream classes and
shows again how streams can be wrapped around one another so that the
output of one stream becomes the input of another. This technique
makes it possible to achieve a great variety of effects. Notice again
the while loop in both methods that does the
actual copying of data from source file to compressed file. These
methods do not attempt to handle exceptions; instead they just pass
them on to the caller, which is often exactly the right thing to do.
Compress is meant to be used as a utility class by
other programs, so it doesn't itself include a
main( ) method. The example does include an inner
Compress.Test class, however, which has a
main( ) method that can test the
gzipFile( ) and zipDirectory( )
methods.
Example 3-5. Compress.java
package je3.io;
import java.io.*;
import java.util.zip.*;
/**
* This class defines two static methods for gzipping files and zipping
* directories. It also defines a demonstration program as a nested class.
**/
public class Compress {
/** Gzip the contents of the from file and save in the to file. */
public static void gzipFile(String from, String to) throws IOException {
// Create stream to read from the from file
FileInputStream in = new FileInputStream(from);
// Create stream to compress data and write it to the to file.
GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(to));
// Copy bytes from one stream to the other
byte[ ] buffer = new byte[4096];
int bytes_read;
while((bytes_read = in.read(buffer)) != -1)
out.write(buffer, 0, bytes_read);
// And close the streams
in.close( );
out.close( );
}
/** Zip the contents of the directory, and save it in the zipfile */
public static void zipDirectory(String dir, String zipfile)
throws IOException, IllegalArgumentException {
// Check that the directory is a directory, and get its contents
File d = new File(dir);
if (!d.isDirectory( ))
throw new IllegalArgumentException("Compress: not a directory: " +
dir);
String[ ] entries = d.list( );
byte[ ] buffer = new byte[4096]; // Create a buffer for copying
int bytes_read;
// Create a stream to compress data and write it to the zipfile
ZipOutputStream out =
new ZipOutputStream(new FileOutputStream(zipfile));
// Loop through all entries in the directory
for(int i = 0; i < entries.length; i++) {
File f = new File(d, entries[i]);
if (f.isDirectory( )) continue; // Don't zip sub-directories
FileInputStream in = new FileInputStream(f); // Stream to read file
ZipEntry entry = new ZipEntry(f.getPath( )); // Make a ZipEntry
out.putNextEntry(entry); // Store entry
while((bytes_read = in.read(buffer)) != -1) // Copy bytes
out.write(buffer, 0, bytes_read);
in.close( ); // Close input stream
}
// When we're done with the whole loop, close the output stream
out.close( );
}
/**
* This nested class is a test program that demonstrates the use of the
* static methods defined above.
**/
public static class Test {
/**
* Compress a specified file or directory. If no destination name is
* specified, append .gz to a file name or .zip to a directory name
**/
public static void main(String args[ ]) throws IOException {
if ((args.length != 1)&& (args.length != 2)) { // check arguments
System.err.println("Usage: java Compress$Test <from> [<to>]");
System.exit(0);
}
String from = args[0], to;
File f = new File(from);
boolean directory = f.isDirectory( ); // Is it a file or directory?
if (args.length == 2) to = args[1];
else { // If destination not specified
if (directory) to = from + ".zip"; // use a .zip suffix
else to = from + ".gz"; // or a .gz suffix
}
if ((new File(to)).exists( )) { // Make sure not to overwrite
System.err.println("Compress: won't overwrite existing file: "+
to);
System.exit(0);
}
// Finally, call one of the methods defined above to do the work.
if (directory) Compress.zipDirectory(from, to);
else Compress.gzipFile(from, to);
}
}
}