[ Team LiB ] Previous Section Next Section

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);
        }
    }
}
    [ Team LiB ] Previous Section Next Section