The Zip File System Provider in JDK7 allows you to treat a zip or jar file as a file system, which means that you can perform operations, such as moving, copying, deleting, renaming etc, just as you would with ordinary files. In previous versions of Java, you would have to use ZipEntry
objects and read/write using ZipInputStream
s and ZipOutputStream
s which was quite messy and verbose. The zip file system makes working with zip files much easier!
This post shows you how to create a zip file and extract/list its contents, all using a zip file system.
Constructing a zip file system:
In order to work with a zip file, you have to construct a "zip file system" first. The method below shows how this is done. You need to pass in a properties map with create=true
if you want the file system to create the zip file if it doesn't exist.
/**
* Returns a zip file system
* @param zipFilename to construct the file system from
* @param create true if the zip file should be created
* @return a zip file system
* @throws IOException
*/
private static FileSystem createZipFileSystem(String zipFilename,
boolean create)
throws IOException {
// convert the filename to a URI
final Path path = Paths.get(zipFilename);
final URI uri = URI.create("jar:file:" + path.toUri().getPath());
final Map<String, String> env = new HashMap<>();
if (create) {
env.put("create", "true");
}
return FileSystems.newFileSystem(uri, env);
}
Once you have a zip file system, you can invoke methods of the java.nio.file.FileSystem
, java.nio.file.Path
and java.nio.file.Files
classes to manipulate the zip file.
Unzipping a Zip File:
In order to extract a zip file, you can walk the zip file tree from the root and copy files to the destination directory. Since you are dealing with a zip file system, extracting a directory is exactly the same as copying a directory recursively to another directory. The code below demonstrates this. (Note the use of the try-with-resources statement to close the zip file system automatically when done.)
/**
* Unzips the specified zip file to the specified destination directory.
* Replaces any files in the destination, if they already exist.
* @param zipFilename the name of the zip file to extract
* @param destFilename the directory to unzip to
* @throws IOException
*/
public static void unzip(String zipFilename, String destDirname)
throws IOException{
final Path destDir = Paths.get(destDirname);
//if the destination doesn't exist, create it
if(Files.notExists(destDir)){
System.out.println(destDir + " does not exist. Creating...");
Files.createDirectories(destDir);
}
try (FileSystem zipFileSystem = createZipFileSystem(zipFilename, false)){
final Path root = zipFileSystem.getPath("/");
//walk the zip file tree and copy files to the destination
Files.walkFileTree(root, new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
final Path destFile = Paths.get(destDir.toString(),
file.toString());
System.out.printf("Extracting file %s to %s\n", file, destFile);
Files.copy(file, destFile, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
final Path dirToCreate = Paths.get(destDir.toString(),
dir.toString());
if(Files.notExists(dirToCreate)){
System.out.printf("Creating directory %s\n", dirToCreate);
Files.createDirectory(dirToCreate);
}
return FileVisitResult.CONTINUE;
}
});
}
}
Creating a Zip File:
The following method shows how to create a zip file from a list of files. If a directory is passed in, it walks the directory tree and copies files into the zip file system:
/**
* Creates/updates a zip file.
* @param zipFilename the name of the zip to create
* @param filenames list of filename to add to the zip
* @throws IOException
*/
public static void create(String zipFilename, String... filenames)
throws IOException {
try (FileSystem zipFileSystem = createZipFileSystem(zipFilename, true)) {
final Path root = zipFileSystem.getPath("/");
//iterate over the files we need to add
for (String filename : filenames) {
final Path src = Paths.get(filename);
//add a file to the zip file system
if(!Files.isDirectory(src)){
final Path dest = zipFileSystem.getPath(root.toString(),
src.toString());
final Path parent = dest.getParent();
if(Files.notExists(parent)){
System.out.printf("Creating directory %s\n", parent);
Files.createDirectories(parent);
}
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
}
else{
//for directories, walk the file tree
Files.walkFileTree(src, new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
final Path dest = zipFileSystem.getPath(root.toString(),
file.toString());
Files.copy(file, dest, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
final Path dirToCreate = zipFileSystem.getPath(root.toString(),
dir.toString());
if(Files.notExists(dirToCreate)){
System.out.printf("Creating directory %s\n", dirToCreate);
Files.createDirectories(dirToCreate);
}
return FileVisitResult.CONTINUE;
}
});
}
}
}
}
Listing the contents of a zip file:
This is the same as extracting a zip file except that instead of copying the files visited, we simply print them out:
/**
* List the contents of the specified zip file
* @param filename
* @throws IOException
* @throws URISyntaxException
*/
public static void list(String zipFilename) throws IOException{
System.out.printf("Listing Archive: %s\n",zipFilename);
//create the file system
try (FileSystem zipFileSystem = createZipFileSystem(zipFilename, false)) {
final Path root = zipFileSystem.getPath("/");
//walk the file tree and print out the directory and filenames
Files.walkFileTree(root, new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
print(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException {
print(dir);
return FileVisitResult.CONTINUE;
}
/**
* prints out details about the specified path
* such as size and modification time
* @param file
* @throws IOException
*/
private void print(Path file) throws IOException{
final DateFormat df = new SimpleDateFormat("MM/dd/yyyy-HH:mm:ss");
final String modTime= df.format(new Date(
Files.getLastModifiedTime(file).toMillis()));
System.out.printf("%d %s %s\n",
Files.size(file),
modTime,
file);
}
});
}
}
Further Reading:
Zip File System Provider