Blog

The Compressed Java Zip Solution

Recently, I had a requirement on a project to systematically zip up a directory of reports to be stored on AWS S3 and later downloaded by the user. I thought it can’t be too hard to zip files using Java with the expectation that the APIs would reflect the use of native OS compression capability.

I was right about it not being too hard, but wrong about how long it would take me to figure it out. I looked at Java Zip, Apache Zip, 7z, and a bunch of other Java zip APIs. I ended up going with Java’s native compression API:

  • It was native to Java.
  • There are bunch of resources (forums and blogs) with compression examples.
  • I was sure it was supported and current, something I couldn’t tell about the other compression libraries.

Eventually, I glued a solution together.

File Compression

The Task

The task is to zip a directory that can contain:

  • Subdirectories with files
  • Files
  • Subdirectories with additional subdirectories

When a user unzips the zip file, the subdirectory and all files should be in the same directory structure as the zip.

The Solution

The solution involves classes from java.util.zip, java.io, and Apache IO commons along with a recursive algorithm.

/**
     * Zips up a directory structure
     * @param zipDirectory The director to zip
     * @param zipFilename The file name to zip up
     * @throws IOException Thrown if there is an issue
     */
    public static void zipDirectory(File zipDirectory, String zipFilename) throws IOException {
        ZipOutputStream zout = null;
        FileOutputStream fout = null;
        File zipFile = new File(zipFilename);
        try {
            fout = new FileOutputStream(zipFile);
            zout = new ZipOutputStream(fout);
            String path = zipDirectory.getName() + File.separator;
            LOGGER.debug("Zip path: {}", path);
            zout.putNextEntry(new ZipEntry(path));
            zipSubDirectory(path, zipDirectory, zout);
            zout.closeEntry();
        } finally {
            IOUtils.closeQuietly(zout);
            IOUtils.closeQuietly(fout);
        }
    }

    private static void zipSubDirectory(String basePath, File dir, ZipOutputStream zout) throws IOException {
        byte[] buffer = new byte[4096];
        File[] files = Optional.of(dir.listFiles()).orElse(new File[0]);
        for (File file : files) {
            if (file.isDirectory()) {
                String path = FilenameUtils.concat(basePath, file.getName()) + File.separator;
                LOGGER.debug("Zip path: {}", path);
                zout.putNextEntry(new ZipEntry(path));
                zipSubDirectory(path, file, zout);
                zout.closeEntry();
            } else {
                FileInputStream fin = null;
                try {
                    fin = new FileInputStream(file);
                    String path = FilenameUtils.concat(basePath, file.getName());
                    LOGGER.debug("Zip path: {}", path);
                    zout.putNextEntry(new ZipEntry(path));
                    int length;
                    while ((length = fin.read(buffer)) > 0) {
                        zout.write(buffer, 0, length);
                    }
                    zout.closeEntry();
                } finally {
                    IOUtils.closeQuietly(fin);
                }
            }
        }
    }

Using the native Java SDK to build a recursive zip solution is the most practical solution for Java file compression. The keys to remember are:

  • Build the zip file relative to the root directory
  • Zip the root directory first
  • Adding a trailing slash at the end of the directory
  • Zip the entries in the order they appear
  • Add a directory before adding any children

Categories: Blog

Tags: , , , , , , , ,

Ryan Van Fleet
02 Aug, 2017


Leave a Reply

Your email address will not be published. Required fields are marked *