ProblemYou need to create and/or extract from a JAR archive or a file in the well-known Zip Archive format, as established by PkZip and used by Unix zip/unzip and WinZip. SolutionYou could use the jar program in the Java Development Kit since its file format is identical to the zip format with the addition of the META-INF directory to contain additional structural information. But since this is a book about programming, you are probably more interested in the ZipFile and ZipEntry classes and the stream classes that they provide access to. DiscussionThe class java.util.zip.ZipFile is not an I/O class per se, but a utility class that allows you to read or write the contents of a JAR or zip-format file.[5] When constructed, it creates a series of ZipEntry objects, one to represent each entry in the archive. In other words, the ZipFile represents the entire archive, and the ZipEntry represents one entry, or one file that has been stored (and compressed) in the archive. The ZipEntry has methods like getName( ), which returns the name that the file had before it was put into the archive, and getInputStream( ) , which gives you an InputStream that will transparently uncompress the archive entry by filtering it as you read it. To create a ZipFile object, you need either the name of the archive file or a File object representing it:
ZipFile zippy = new ZipFile(fileName); If you want to see whether a given file is present in the archive, you can call the getEntry( ) method with a filename. More commonly, you'll want to process all the entries; for this, use the ZipFile object to get a list of the entries in the archive, in the form of an Enumeration (see Recipe 7.4): Enumeration all = zippy.entries( ); while (all.hasMoreElements( )) { ZipEntry entry = (ZipEntry)all.nextElement( ); We can then process each entry as we wish. A simple listing program could be: if (entry.isDirectory( )) println("Directory: " + e.getName( )); else println("File: " + e.getName( )); A fancier version would extract the files. The program in Example 10-8 does both: it lists by default, but with the -x (extract) switch, it actually extracts the files from the archive. Example 10-8. UnZip.javaimport java.io.*; import java.util.*; import java.util.zip.*; /** * UnZip -- print or unzip a JAR or PKZIP file using java.util.zip. * Final command-line version: extracts files. */ public class UnZip { /** Constants for mode listing or mode extracting. */ public static final int LIST = 0, EXTRACT = 1; /** Whether we are extracting or just printing TOC */ protected int mode = LIST; /** The ZipFile that is used to read an archive */ protected ZipFile zippy; /** The buffer for reading/writing the ZipFile data */ protected byte[] b; /** Simple main program, construct an UnZipper, process each * .ZIP file from argv[] through that object. */ public static void main(String[] argv) { UnZip u = new UnZip( ); for (int i=0; i<argv.length; i++) { if ("-x".equals(argv[i])) { u.setMode(EXTRACT); continue; } String candidate = argv[i]; // System.err.println("Trying path " + candidate); if (candidate.endsWith(".zip") || candidate.endsWith(".jar")) u.unZip(candidate); else System.err.println("Not a zip file? " + candidate); } System.err.println("All done!"); } /** Construct an UnZip object. Just allocate the buffer */ UnZip( ) { b = new byte[8092]; } /** Set the Mode (list, extract). */ protected void setMode(int m) { if (m == LIST || m == EXTRACT) mode = m; } /** For a given Zip file, process each entry. */ public void unZip(String fileName) { try { zippy = new ZipFile(fileName); Enumeration all = zippy.entries( ); while (all.hasMoreElements( )) { getFile((ZipEntry)all.nextElement( )); } } catch (IOException err) { System.err.println("IO Error: " + err); return; } } /** Process one file from the zip, given its name. * Either print the name, or create the file on disk. */ protected void getFile(ZipEntry e) throws IOException { String zipName = e.getName( ); if (mode == EXTRACT) { // double-check that the file is in the zip // if a directory, mkdir it (remember to // create intervening subdirectories if needed!) if (zipName.endsWith("/")) { new File(zipName).mkdirs( ); return; } // Else must be a file; open the file for output System.err.println("Creating " + zipName); FileOutputStream os = new FileOutputStream(zipName); InputStream is = zippy.getInputStream(e); int n = 0; while ((n = is.read(b)) >0) os.write(b, 0, n); is.close( ); os.close( ); } else // Not extracting, just list if (e.isDirectory( )) { System.out.println("Directory " + zipName); } else { System.out.println("File " + zipName); } } } See AlsoJDK 1.5 introduces a new format, called Pack200, that is specified as the JSR 200 Network File Transfer Format. See http://jcp.org/en/jsr/detail?id=200 for details. The new class javax.pack.Pack200 converts between JAR and Pack200 formats. |