Zip and Unzip Files in Java

In this tutorial, I’m going to guide you through the process of zipping and unzipping files in Java. This is a common task in many programming and data management scenarios, and it’s a skill that can be incredibly useful in a variety of contexts.

Throughout this tutorial, you’ll learn how to zip a single file, create a password-protected zip archive, zip multiple files, and even zip an entire directory in Java. But we won’t stop there. I’ll also show you how to add more files to an existing zip archive, and how to unzip files to both a current directory and a destination directory of your choice. By the end of this tutorial, you’ll have a solid understanding of these processes and be able to implement them in your own Java projects. Let’s get started!

Zip a File in Java

Java provides support for zipping files through the java.util.zip package. This package contains classes like ZipOutputStream and ZipEntry which are instrumental in the zipping process.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipFile {
    
    public static void main(String[] args) {
        
        String sourceFile = "fileToZip.txt";
        FileOutputStream fos = null;
        ZipOutputStream zipOutputStream = null;
        FileInputStream fis = null;
        
        try {
            fos = new FileOutputStream("compressedFile.zip");
            zipOutputStream = new ZipOutputStream(fos);
            
            File fileToZip = new File(sourceFile);
            fis = new FileInputStream(fileToZip);
            
            ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
            zipOutputStream.putNextEntry(zipEntry);
            
            byte[] bytes = new byte[1024];
            int length;
            
            while ((length = fis.read(bytes)) >= 0) {
                zipOutputStream.write(bytes, 0, length);
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
                if (fis != null) {
                    fis.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Where:

  • A FileOutputStream is created to write the output to a file named “compressedFile.zip”.
  • A ZipOutputStream is created from the FileOutputStream instance to handle the zipping process.
  • A File object is created for the source file and a FileInputStream is created to read this file.
  • A ZipEntry object is created with the name of the source file. This represents an entry in the zip file.The ZipEntry class in Java is part of the java.util.zip package. It’s used to represent a zip file entry, which can be either a single file or a directory that’s been compressed (zipped).When you’re creating a zip file, you create a ZipEntry for each file or directory that you want to add to the zip file. You then write the ZipEntry to a ZipOutputStream.
  • The putNextEntry() method of ZipOutputStream is called with the ZipEntry object. This method signals that we’re starting a new entry in the zip file.
  • The contents of the source file are read and written to the ZipOutputStream, effectively adding them to the zip file.
  • Finally, all streams are closed to free up resources.

Creating a Password-Protected Zip Archive in Java

In this section, I’ll guide you on how to create a password-protected zip archive in Java. This can be useful when you want to add an extra layer of security to your zipped files. Unfortunately, Java’s built-in libraries do not support the creation of password-protected zip files. However, we can use a third-party library called Apache Commons Compress to achieve this.

First, you need to add the Apache Commons Compress library to your project. If you’re using Maven, you can add the following dependency to your pom.xml file:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.23</version> <!-- Check for the latest version -->
</dependency>

Now, let’s look at how to create a password-protected zip file:

import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        String fileName = "fileToZip.txt";
        String zipFileName = "compressed.zip";
        String password = "password";

        try (OutputStream os = new FileOutputStream(zipFileName);
             ZipArchiveOutputStream zaos = new ZipArchiveOutputStream(os)) {
            zaos.setPassword(password.toCharArray());
            ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
            zaos.putArchiveEntry(entry);

            try (InputStream is = new FileInputStream(fileName)) {
                IOUtils.copy(is, zaos);
            }

            zaos.closeArchiveEntry();
        }
    }
}

Here’s what the code does:

  • We first create a FileOutputStream for the zip file.
  • We then create a ZipArchiveOutputStream from the FileOutputStream. This is similar to ZipOutputStream but provides additional features, including support for password protection.
  • We set the password for the zip file using the setPassword() method.
  • We create a ZipArchiveEntry for the file we want to zip and add it to the ZipArchiveOutputStream using the putArchiveEntry() method.
  • We then read the contents of the file using a FileInputStream and write it to the ZipArchiveOutputStream using the IOUtils.copy() method from Apache Commons Compress.
  • Finally, we close the archive entry and the ZipArchiveOutputStream.

And that’s it!

You’ve just created a password-protected zip file in Java. In the next section, we’ll learn how to zip multiple files.

How to Zip Multiple Files in Java?

In this section, I’ll guide you on how to zip multiple files in Java. This can be useful when you want to compress several files into a single zip archive for easier storage or transmission.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Main {
    public static void main(String[] args) {
        String[] srcFiles = {"srcFile1.txt", "srcFile2.txt", "srcFile3.txt"};
        try {
            // create byte buffer
            byte[] buffer = new byte[1024];

            FileOutputStream fos = new FileOutputStream("multiCompressed.zip");

            ZipOutputStream zos = new ZipOutputStream(fos);

            for (String srcFile : srcFiles) {

                File aFile = new File(srcFile);
                FileInputStream fis = new FileInputStream(aFile);

                // begin writing a new ZIP entry, positions the stream to the start of the entry data
                zos.putNextEntry(new ZipEntry(aFile.getName()));

                int length;

                while ((length = fis.read(buffer)) > 0) {
                    zos.write(buffer, 0, length);
                }

                // close the current entry
                zos.closeEntry();

                // close the InputStream
                fis.close();
            }

            // close the ZipOutputStream
            zos.close();

        } catch (IOException ioe) {
            System.out.println("Error creating zip file: " + ioe);
        }
    }
}

Here’s what the code does:

  • We first create an array of strings, each representing a source file that we want to zip.
  • We then create a FileOutputStream for the zip file and a ZipOutputStream from the FileOutputStream.
  • We loop over each source file, create a File object for it, and open a FileInputStream.
  • For each file, we create a new ZipEntry and add it to the ZipOutputStream using the putNextEntry() method.
  • We then read the contents of the file and write it to the ZipOutputStream.
  • After we’re done with a file, we close the current zip entry and the FileInputStream.
  • After we’re done with all files, we close the ZipOutputStream.

Please note that in this example, the source files are located in the same directory as the Java program. If your files are located in a different directory, you’ll need to specify the complete path to the files in the srcFiles array. For example, if you have a file named example.txt in a directory named documents, you would specify it as documents/example.txt.

And that’s it! You’ve just zipped multiple files in Java. In the next section, we’ll learn how to zip a directory.

How to Zip a Directory in Java?

In this section, you will learn how to zip an entire directory in Java. This can be useful when you want to compress a directory containing multiple files and subdirectories into a single zip archive for easier storage or transmission.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Main {
    public static void main(String[] args) throws IOException {
        File directoryToZip = new File("directoryPath"); // Replace "directoryPath" with the path to the directory that you want to zip.
        FileOutputStream fos = new FileOutputStream("directoryCompressed.zip");
       
        ZipOutputStream zos = new ZipOutputStream(fos);
        zipDirectory(directoryToZip, directoryToZip.getName(), zos);
        zos.close();
    }

    private static void zipDirectory(File directory, String basePath, ZipOutputStream zos) throws IOException {
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                zipDirectory(file, basePath + "/" + file.getName(), zos);
            } else {
                zipFile(file, basePath, zos);
            }
        }
    }

    private static void zipFile(File file, String basePath, ZipOutputStream zos) throws IOException {
        zos.putNextEntry(new ZipEntry(basePath + "/" + file.getName()));
        FileInputStream fis = new FileInputStream(file);
        byte[] buffer = new byte[1024];
        int read = 0;
        while ((read = fis.read(buffer)) != -1) {
            zos.write(buffer, 0, read);
        }
        fis.close();
        zos.closeEntry();
    }
}

Here’s what the code does:

  • We first create a File object for the directory that we want to zip.
  • We then create a FileOutputStream for the zip file and a ZipOutputStream from the FileOutputStream.
  • We call the zipDirectory method, which recursively zips the directory and its subdirectories.
  • In the zipDirectory method, we list all the files in the directory. For each file, if it’s a directory, we recursively call zipDirectory; if it’s a file, we call zipFile.
  • The zipFile method creates a new ZipEntry and adds it to the ZipOutputStream. It then reads the contents of the file and writes it to the ZipOutputStream.
  • After we’re done with all files and directories, we close the ZipOutputStream.

Please note that you need to replace "directoryPath" with the path to the directory that you want to zip.

Adding Files to Existing Zip Archive in Java

In this section, you will learn how to add more files to an existing zip archive in Java. This can be useful when you want to update a zip archive by adding new files to it.

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        String fileToAdd = "path/to/fileToAdd.txt";
        String zipFilePath = "path/to/existingArchive.zip";

        Map<String, String> env = new HashMap<>();
        env.put("create", "true");

        Path path = Paths.get(zipFilePath);
        URI uri = URI.create("jar:" + path.toUri());

        try (FileSystem fs = FileSystems.newFileSystem(uri, env)) {
            Path sourceFile = Paths.get(fileToAdd);
            Path destinationFile = fs.getPath("/" + sourceFile.getFileName());

            Files.copy(sourceFile, destinationFile, StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • Define the files to add and the zip file: The filesToAdd array contains the paths to the files that you want to add to the zip file. The zipFilePath string contains the path to the existing zip file that you want to add the files to.
  • Set up the environment for the zip file system: The env map is used to set up the environment for the zip file system. The “create” option is set to “true”, which means that a new file will be created if the zip file doesn’t exist.
  • Create a URI for the zip file system: The path object is a Path to the zip file. The uri object is a URI that represents the zip file system. The “jar:” scheme is used to indicate that this is a zip file.
  • Create the zip file system: The FileSystem is created for the zip file. This allows us to interact with the zip file as if it were a file system.
  • Loop over the files to add: For each file in the filesToAdd array, the code does the following:
  • Get the source file and the destination file: The sourceFile object is a Path to the file to add. The destinationFile object is a Path in the zip file system for the new file. The new file will have the same name as the file to add and will be located at the root of the zip file.
  • Copy the file to the zip file: The Files.copy method is used to copy the file to add to the new file in the zip file. If a file with the same name already exists in the zip file, it will be replaced.
  • Handle exceptions: If an IOException occurs during the process, the stack trace of the exception will be printed.

Please note that you need to replace "path/to/fileToAdd1.txt", "path/to/fileToAdd2.txt", and "path/to/existingArchive.zip" with the paths to the actual files and zip file.

Unzip Archive to a Destination Directory

In this section, I’ll guide you on how to unzip files to a specific destination directory in Java. This can be useful when you want to extract the contents of a zip archive to a specific location. Additionally, I’ll also show you how to unzip files to the current directory.

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class UnzipFile {
    
     public static void main(String[] args) {
        String zipFilePath = "/Users/sergeykargopolov/NetBeansProjects/ZipFile/compressedFile.zip";
        String destDirectory = "/Users/sergeykargopolov/NetBeansProjects/ZipFile/unzip";

        try {
            unzip(zipFilePath, destDirectory);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
     
    public static void unzip(String zipFilePath, String destDirectory) throws IOException {
        ZipFile zipFile = new ZipFile(zipFilePath);
        Enumeration<? extends ZipEntry> entries = zipFile.entries();

        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            Path entryDestination = Paths.get(destDirectory, entry.getName());

            if (entry.isDirectory()) {
                Files.createDirectories(entryDestination);
            } else {
                Files.createDirectories(entryDestination.getParent());
                try (InputStream in = zipFile.getInputStream(entry)) {
                    Files.copy(in, entryDestination);
                }
            }
        }
    }
}

Here’s a brief explanation of what the above code does:

  • Define the zip file and the destination directory: The zipFilePath is the path to the zip file that you want to unzip, and destDirectory is the path to the directory where you want to extract the files.
  • Open the zip file: The ZipFile object zipFile is created from the zipFilePath. This object represents the zip file that you want to unzip.
  • Get the entries in the zip file: The Enumeration object entries is obtained by calling the entries() method on the zipFile object. This object represents all the entries (i.e., files and directories) in the zip file.
  • Iterate over the entries in the zip file: The while loop iterates over each entry in the zip file. For each entry, it does the following:
    • Get the destination path for the entry: The Path object entryDestination is created from the destDirectory and the name of the entry. This object represents the path where the entry will be extracted.
    • Check if the entry is a directory: If the entry is a directory, it creates the directory and any necessary parent directories. If the entry is a file, it creates any necessary parent directories, opens an InputStream for the entry, and copies the contents of the entry to the destination path.

This code will unzip the specified zip file to the specified destination directory. If a file with the same name already exists in the destination directory, it will be replaced.

If you want to unzip the files to a different directory, you can simply pass "." as the destination directory. For example:

unzip("path/to/archive.zip", ".");

Common Errors and Troubleshooting

In this section, we’ll discuss some common errors you might encounter while working with zip and unzip operations in Java, and how to troubleshoot them.

  1. FileNotFoundException: This error occurs when the file you’re trying to zip or unzip doesn’t exist at the specified location. To troubleshoot this, make sure the file path is correct and the file exists at that location.
  2. ZipException: no current ZIP entry: This error occurs when you’re trying to read an entry from a ZipInputStream but there’s no current entry selected. This usually happens when you forget to call getNextEntry() before trying to read the entry. To fix this, always call getNextEntry() and check that it doesn’t return null before trying to read the entry.
  3. IOException: Entry is outside of the target dir: This error occurs when unzipping a file and the zip entry is trying to create a file outside of the target directory. This is a security feature to prevent Zip Slip attacks. To fix this, ensure that the zip file doesn’t contain entries with relative paths that go outside of the target directory.
  4. ZipException: invalid entry compressed size: This error occurs when the zip file is corrupted and the compressed size of an entry doesn’t match the actual size. Unfortunately, there’s not much you can do to fix a corrupted zip file. You can try to open it with a different tool to see if it can handle the corruption, but in most cases, you’ll need to get a new copy of the file.
  5. OutOfMemoryError: This error occurs when the JVM runs out of memory. This can happen if you’re trying to zip or unzip a very large file and your JVM doesn’t have enough memory. To fix this, you can increase the amount of memory available to the JVM by using the -Xmx option when starting your program. For example, java -Xmx1024m MyProgram will start MyProgram with a maximum of 1024 megabytes of memory.

Remember, when working with zip files in Java, always make sure to close your streams in a finally block or use a try-with-resources statement to ensure they get closed even if an exception occurs. This will prevent resource leaks and can help avoid some errors.

Conclusion

That concludes our comprehensive tutorial on working with zip and unzip operations in Java. We’ve covered everything from zipping a single file to unzipping files to a destination directory, and even troubleshooting common errors. With these skills, you’re well-equipped to handle file compression tasks in your Java applications.

But don’t stop here! There’s always more to learn in the world of Java programming. For more practical examples and in-depth guides, I highly recommend visiting Java Example Programs on the Apps Developer Blog. It’s a fantastic resource filled with a wealth of knowledge to help you further enhance your Java skills.

Happy coding!