How to Unzip a ZIP File in Java

In this tutorial, we will learn how to unzip a ZIP file using Java’s built-in java.util.zip package, while also discussing the Zip Slip vulnerability and how to avoid it.

Table of Contents:

1. Extracting a ZIP File in Java

We can use the ZipInputStream class from java.util.zip to extract files from a ZIP archive. Here’s a step-by-step example:

UnzipUtility.java

package com.mkyong.unzip;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class UnzipUtility {

    public static void unzip(String zipFilePath, String destDirectory) 
            throws IOException {

        Path destDirPath = Paths.get(destDirectory);
        if (!Files.exists(destDirPath)) {
            Files.createDirectories(destDirPath);
        }

        try (ZipInputStream zipIn = new ZipInputStream(
                Files.newInputStream(Paths.get(zipFilePath)))) {
            ZipEntry entry = zipIn.getNextEntry();

            while (entry != null) {
                Path outPath = destDirPath.resolve(entry.getName()).normalize();

                // Prevent Zip Slip vulnerability
                if (!outPath.startsWith(destDirPath)) {
                    throw new IOException("Entry is outside of target dir: " 
                            + entry.getName());
                }

                if (entry.isDirectory()) {
                    Files.createDirectories(outPath);
                } else {
                    extractFile(zipIn, outPath);
                }
                zipIn.closeEntry();
                entry = zipIn.getNextEntry();
            }
        }
    }

    private static void extractFile(ZipInputStream zipIn, Path outPath) 
            throws IOException {

        // Ensure parent directories exist before creating the file
        // Create all necessary parent directories before writing the file.
        // Prevents NoSuchFileException for nested folders like /folder1/folder2/file.txt
        Files.createDirectories(outPath.getParent());

        try (BufferedOutputStream bos = new BufferedOutputStream(
                Files.newOutputStream(outPath))) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = zipIn.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
        }
    }

    public static void main(String[] args) {
        String zipFilePath = "files.zip";
        String destDir = "outputDir";
        try {
            unzip(zipFilePath, destDir);
            System.out.println("Unzip successful!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. Understanding the Zip Slip Vulnerability

What is Zip Slip?

Zip Slip is a path traversal vulnerability that allows an attacker to overwrite files outside the intended extraction directory. It happens when malicious ZIP archives contain filenames like:


../../../../etc/passwd

Without validation, this can overwrite critical system files.

How to Prevent Zip Slip?

To mitigate this risk, we must:

  • Use Path#normalize() to resolve and sanitize paths.
  • Ensure extracted paths stay within the intended directory using startsWith().

Secure Validation Code

We can add the following check in our code to prevent Zip Slip:


    Path outPath = destDirPath.resolve(entry.getName()).normalize();
    
    if (!outPath.startsWith(destDirPath)) {
        throw new IOException("Entry is outside of target dir: " + entry.getName());
    }

This ensures extracted files remain inside the target directory, preventing path traversal attacks.

Legacy java.io.* Validation Code

For the legacy java.io.*, we can add the following check in our code to prevent Zip Slip:


    String canonicalDestination = destDir.getCanonicalPath();
    String canonicalOutFile = outFile.getCanonicalPath();
    
    if (!canonicalOutFile.startsWith(canonicalDestination)) {
        throw new IOException("Entry is outside of target dir: " + entry.getName());
    }

3. References

59 comments on “How to Unzip a ZIP File in Java

  1. The sourcecode is wrong. No offense, just pointing out the mistake. On win8 when there’s folder inside zip file, the code looks for parent dir and do not asks if the current “file” is a dir, therefore the dir is created as “file” with 0 size and without any file suffix, and then the attempt to create now the parent folder – with the same name as the 0 size file – will fail and first attempt to actually unzip file into that folder throws an exception. Before calling the FileOutputStream, make sure the file is not a folder (dot in filename for example).

    1. Thanks for your feedback.

      We updated the article with the latest example to unzip a file, and the above comment is irrelevant now. 

  2. How to unzip folder having file names with special characters for e.g. test.zip having file with name fndStaticLookups_finder=LookupTypeFinder%3BBindLookupType%3DACTIVITY_SHOWTIMEAS_CD.json

  3. Hi mkyong
    I have a zip file which contains multiple CSV files and some folders. These folders also contains CSV files.
    In a word, I wonder how to deal with this kind of zip file recursively?

  4. private static final String INPUT_ZIP_FILE = “/classes/sds.zip”;
    private static final String OUTPUT_FOLDER = “/classes/testdata/

    Exception :::

    java.io.FileNotFoundException: sds.zip (No such file or directory)
    at java.io.FileInputStream.open0(Native Method)
    at java.io.FileInputStream.open(FileInputStream.java:195)
    at java.io.FileInputStream.(FileInputStream.java:138)
    at java.io.FileInputStream.(FileInputStream.java:93)
    at com.test.zip.UnZip.unZipIt(UnZip.java:43)
    at com.test.zip.UnZip.main(UnZip.java:19)

    1. We updated the unzip example with directory checking.

      if (zipEntry.getName().endsWith(File.separator)) {
       isDirectory = true;
      }

  5. FileOutputStream fos = new FileOutputStream(newFile);

    int len;
    while ((len = zis.read(buffer)) > 0) {
    fos.write(buffer, 0, len);
    }

    fos.close();

    Wouldn’t a BufferedWriter probably be faster ? Just a thought. Thanks for the tuto.

    1. I didn’t test the performance, and the Files.copy is the easiest way to copy a file. May be someone can feedback on this.

  6. //— Here is a fix so that is creates when there are sub-directories .. this creates if needed
    // Note: If you had run it before, remove the existing files because there may be a blank file holding the spot of the directory

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

    import org.apache.commons.io.FilenameUtils;

    public class FileUnzipper {
    List fileList;

    /*
    * private static final String INPUT_ZIP_FILE = “C:\MyFile.zip”; private
    * static final String OUTPUT_FOLDER = “C:\outputzip”;
    *
    * public static void main( String[] args ) { FileUnzipper unZip = new
    * FileUnzipper(); unZip.unZipIt(INPUT_ZIP_FILE,OUTPUT_FOLDER); }
    *
    */
    /**
    * Unzip it
    *
    * @param zipFile
    * input zip file
    * @param output
    * zip file output folder
    */
    public static boolean unZipIt(String zipFile, String outputFolder) {
    boolean tmpRet = true;
    byte[] buffer = new byte[1024];

    try {

    // create output directory is not exists
    File folder = new File(outputFolder);
    if (!folder.exists()) {
    folder.mkdir();
    }

    // get the zip file content
    ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
    // get the zipped file list entry
    ZipEntry ze = zis.getNextEntry();

    while (ze != null) {

    String fileName = ze.getName();
    File newFile = new File(outputFolder + File.separator + fileName);

    String tmpCheckFN = FilenameUtils.removeExtension(fileName);

    if (tmpCheckFN.equalsIgnoreCase(fileName)) {
    // — Directory, create if needed
    if (!newFile.exists()) {
    System.out.println(“Create dir ” + outputFolder + File.separator + fileName);
    boolean success = newFile.mkdirs();
    if (!success) {
    System.err.println(“Could not create directory for ” + fileName + “.”);
    return false;
    }
    }
    } else {
    System.out.println(“file unzip : ” + newFile.getAbsoluteFile());

    new File(newFile.getParent()).mkdirs();

    FileOutputStream fos = new FileOutputStream(newFile);

    int len;
    while ((len = zis.read(buffer)) > 0) {
    fos.write(buffer, 0, len);
    }

    fos.close();
    }

    ze = zis.getNextEntry();
    }

    zis.closeEntry();
    zis.close();

    System.out.println(“Done”);

    } catch (IOException ex) {
    tmpRet = false;
    ex.printStackTrace();
    }

    return tmpRet;
    }
    }

    1. For embedded zip or zip inside a zip file, I’m not sure what is the general practice.

      The code needs to be modified to support the embedded zip file.
      if(zipEntry.getName().endsWith(".zip")){
        // unzip it again...
      }

      I haven’t tested the above idea, but it should work; if someone has time, please test it and comment here. 

  7. Hi,

    When i run the code with the ZIP file of mediumsize but when my ZIP file is in the size of GB’s the code fails with File Not Found Exception.

    Can anyone help me to resolve my issue??

  8. java.util.zip.ZipException: invalid entry compressed size (expected 4294967295 but got 14454437 bytes)

    I am getting this error.How do i solve it?

  9. Somehow I can’t make your code to work.
    – If a folder in the zip file has a . in its name, it is read as a file and stuck there
    – If we have a folder inside a folder, there will be errors in various cases
    – Your code only works if there’s only 1 folder containing all the files

  10. Mkyong, your advice has been helpful in learning java. I do have a question, how can I read a zip file that contains other zip files? I need to process all files within a zip file that contains other zip files. I want to process (read) all log files. My issues is that my loop doesn’t know how to deal with an embedded zip file. I’l using java zipfile routines.

  11. Hey man, you are awsome. actually this programm saves my time a lot and i change it a bit so that it can go to a directory and fild all zip files and unzip them. thanks again

  12. I’m getting the below error
    Exception in thread “main” java.lang.IllegalArgumentException: MALFORMED

    when I have a complex zip file (zip inside zip or mix files and folders inside zip)

  13. Hi this is nice tutorial,
    what if I want to check if file exists with same size then skip that file unzipping and move ahead while unzipping

  14. I tried this code , It works perfectly with .zip files only. but not with .rar. It returns null. If I want extract data from .rar file so what to do?

  15. Hi,

    Please can you guide me, how can i convert packed field to unpack field. packed field is coming from mainframe.

    thanks in advance.

  16. Same can be achieved out of the box using Ant Compress library

    http://ant.apache.org/antlibs/compress/

    Maven dependency:-

    <dependency>
    	<groupId>org.apache.ant</groupId>
    	<artifactId>ant-compress</artifactId>
    	<version>1.2</version>
    </dependency>
    
    Unzip unzipper = new Unzip();
    unzipper.setSrc(theZIPFile);
    unzipper.setDest(theTargetFolder);
    unzipper.execute();
    
  17. Your site is simply wonderful. Small snippets so useful to use/understand as part of a bigger code. Appreciate it and do let me know if there is anyway I can contribute it to the site in anyway.

  18. Not sure if this is the right place to post this but maybe it will help somebody.
    Mine problem was that i had a zip inside of my runnable jar file that needed to be extracted . The zip had folders inside.
    how to get the zip file from inside the jar:

    InputStream stream;
    stream = this.getClass().getClassLoader().getSystemResourceAsStream("resources/Sounds.zip");
    

    where the resources is the folder/package inside of the root of the jar
    now is the rest of the code (a little modification of the original one)

    
    package system;
     
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.List;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipInputStream;
     
    public class UnZip
    {
        List<String> fileList;
     
        UnZip(InputStream stream, File dest)
        {
        	unZipIt(stream,dest); // the stream i got from the first part and dest as a file
        }
     
        /**
         * Unzip it
         * @param stream input zip file
         * @param output zip file output folder
         */
        public void unZipIt(InputStream stream, File dest){
     
         byte[] buffer = new byte[1024];
     
         try{
     
        	//create output directory is not exists
        	File folder = dest;
        	if(!folder.exists()){
        		folder.mkdir();
        	}
     
        	//get the zip file content
        	ZipInputStream zis = 
        		new ZipInputStream(stream);
        	//get the zipped file list entry
        	ZipEntry ze = zis.getNextEntry();
     
        	while(ze!=null){
     
        	   String fileName = ze.getName();
               File newFile = new File(dest + File.separator + fileName);
     
               System.out.println("file unzip : "+ newFile.getAbsoluteFile());
     
                //create all non exists folders
                //else you will hit FileNotFoundException for compressed folder
     
               if(ze.isDirectory()) 
               {
            	   new File(newFile.getParent()).mkdirs();
               }
               else
               {
            	FileOutputStream fos = null;
     
                new File(newFile.getParent()).mkdirs();
     
                fos = new FileOutputStream(newFile);             
     
                int len;
                while ((len = zis.read(buffer)) > 0) 
                {
           		fos.write(buffer, 0, len);
                }
     
                fos.close();   
               }
     
               ze = zis.getNextEntry();
        	}
     
            zis.closeEntry();
        	zis.close();
     
        	System.out.println("Done");
     
        }catch(IOException ex){
           ex.printStackTrace(); 
        }
       }    
    }
    

    with this i was able to extract files and folders from the zip to a desired location.
    Hope it can prevent some headToWall love <3 🙂

    1. Thanks a lot! I was happy that someone posted a solution to the same problem as I had. There should be more people like you. 🙂

Leave a Comment

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