'Java ZIP - how to unzip folder?

Is there any sample code, how to particaly unzip folder from ZIP into my desired directory? I have read all files from folder "FOLDER" into byte array, how do I recreate from its file structure?



Solution 1:[1]

I am not sure what do you mean by particaly? Do you mean do it yourself without of API help?

In the case you don't mind using some opensource library, there is a cool API for that out there called zip4J

It is easy to use and I think there is good feedback about it. See this example:

String source = "folder/source.zip";
String destination = "folder/source/";   

try {
    ZipFile zipFile = new ZipFile(source);
    zipFile.extractAll(destination);
} catch (ZipException e) {
    e.printStackTrace();
}

If the files you want to unzip have passwords, you can try this:

String source = "folder/source.zip";
String destination = "folder/source/";
String password = "password";

try {
    ZipFile zipFile = new ZipFile(source);
    if (zipFile.isEncrypted()) {
        zipFile.setPassword(password);
    }
    zipFile.extractAll(destination);
} catch (ZipException e) {
    e.printStackTrace();
}

I hope this is useful.

Solution 2:[2]

Here is the code I'm using. Change BUFFER_SIZE for your needs.

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

public final class ZipUtils {

    private static final int BUFFER_SIZE = 4096;

    public static void extract(ZipInputStream zip, File target) throws IOException {
        try {
            ZipEntry entry;

            while ((entry = zip.getNextEntry()) != null) {
                File file = new File(target, entry.getName());

                if (!file.toPath().normalize().startsWith(target.toPath())) {
                    throw new IOException("Bad zip entry");
                }

                if (entry.isDirectory()) {
                    file.mkdirs();
                    continue;
                }

                byte[] buffer = new byte[BUFFER_SIZE];
                file.getParentFile().mkdirs();
                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file));
                int count;

                while ((count = zip.read(buffer)) != -1) {
                    out.write(buffer, 0, count);
                }

                out.close();
            }
        } finally {
            zip.close();
        }
    }

}

Solution 3:[3]

A most concise, library-free, Java 7+ variant:

public static void unzip(InputStream is, Path targetDir) throws IOException {
    targetDir = targetDir.toAbsolutePath();
    try (ZipInputStream zipIn = new ZipInputStream(is)) {
        for (ZipEntry ze; (ze = zipIn.getNextEntry()) != null; ) {
            Path resolvedPath = targetDir.resolve(ze.getName()).normalize();
            if (!resolvedPath.startsWith(targetDir)) {
                // see: https://snyk.io/research/zip-slip-vulnerability
                throw new RuntimeException("Entry with an illegal path: " 
                        + ze.getName());
            }
            if (ze.isDirectory()) {
                Files.createDirectories(resolvedPath);
            } else {
                Files.createDirectories(resolvedPath.getParent());
                Files.copy(zipIn, resolvedPath);
            }
        }
    }
}

The createDirectories is needed in both branches because zip files not always contain all the parent directories as a separate entries, but might contain them only to represent empty directories.

The code addresses the ZIP-slip vulnerability, it fails if some ZIP entry would go outside of the targetDir. Such ZIPs are not created using the usual tools and are very likely hand-crafted to exploit the vulnerability.

Solution 4:[4]

Same can be achieved using Ant Compress library. It will preserve the folder structure.

Maven dependency:-

<dependency>
    <groupId>org.apache.ant</groupId>
    <artifactId>ant-compress</artifactId>
    <version>1.2</version>
</dependency>

Sample code:-

Unzip unzipper = new Unzip();
unzipper.setSrc(theZIPFile);
unzipper.setDest(theTargetFolder);
unzipper.execute();

Solution 5:[5]

Here's an easy solution which follows more modern conventions. You may want to change the buffer size to be smaller if you're unzipping larger files. This is so you don't keep all of the files info in-memory.

    public static void unzip(File source, String out) throws IOException {
    try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source))) {

        ZipEntry entry = zis.getNextEntry();

        while (entry != null) {

            File file = new File(out, entry.getName());

            if (entry.isDirectory()) {
                file.mkdirs();
            } else {
                File parent = file.getParentFile();

                if (!parent.exists()) {
                    parent.mkdirs();
                }

                try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {

                    int bufferSize = Math.toIntExact(entry.getSize());
                    byte[] buffer = new byte[bufferSize > 0 ? bufferSize : 1];
                    int location;

                    while ((location = zis.read(buffer)) != -1) {
                        bos.write(buffer, 0, location);
                    }
                }
            }
            entry = zis.getNextEntry();
        }
    }
}

Solution 6:[6]

This is the code I used to unzip a zip file with multiple directories. No external libraries used.

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class UnzipFile
{
  public static void main(String[] args) throws IOException
  {
    String fileZip = "src/main/resources/abcd/abc.zip";
    File destDir = new File("src/main/resources/abcd/abc");

    try (ZipFile file = new ZipFile(fileZip))
    {
      Enumeration<? extends ZipEntry> zipEntries = file.entries();
      while (zipEntries.hasMoreElements())
      {
        ZipEntry zipEntry = zipEntries.nextElement();
        File newFile = new File(destDir, zipEntry.getName());

        //create sub directories
        newFile.getParentFile().mkdirs();

        if (!zipEntry.isDirectory())
        {
          try (FileOutputStream outputStream = new FileOutputStream(newFile))
          {
            BufferedInputStream inputStream = new BufferedInputStream(file.getInputStream(zipEntry));
            while (inputStream.available() > 0)
            {
              outputStream.write(inputStream.read());
            }
            inputStream.close();
          }
        }

      }
    }
  }

}

Solution 7:[7]

Here is more "modern" complete code based on this post but refactored (and using Lombok):

import lombok.var;
import lombok.val;

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

import static java.nio.file.Files.createDirectories;

public class UnZip
{
    public static void unZip(String sourceZipFile, String outputDirectory) throws IOException
    {
        val folder = new File(outputDirectory);
        createDirectories(folder.toPath());

        try (val zipInputStream = new ZipInputStream(new FileInputStream(sourceZipFile, Charset.forName("Cp437"))))
        {
            var nextEntry = zipInputStream.getNextEntry();

            while (nextEntry != null)
            {
                val fileName = nextEntry.getName();
                val newFile = new File(outputDirectory + File.separator + fileName);

                newFile.getParentFile().mkdirs();

                if(fileName.endsWith("/")){
                    newFile.mkdirs();
                } else {
                    writeFile(zipInputStream, newFile);
                }

                writeFile(zipInputStream, newFile);

                nextEntry = zipInputStream.getNextEntry();
            }

            zipInputStream.closeEntry();
        }
    }

    private static void writeFile(ZipInputStream inputStream, File file) throws IOException
    {
        val buffer = new byte[1024];
        file.createNewFile();
        try (val fileOutputStream = new FileOutputStream(file))
        {
            int length;
            while ((length = inputStream.read(buffer)) > 0)
            {
                fileOutputStream.write(buffer, 0, length);
            }
        }
    }
}

Solution 8:[8]

You should get all entries from your zip file:

Enumeration entries = zipFile.getEntries();

Then iterating over this enumeration get the ZipEntry from it, check whether it is a directory or not, and create directory or just extract a file respectively.

Solution 9:[9]

After using the other libraries I stumbled upon this one: https://github.com/thrau/jarchivelib

Far superior.

Gradle: implementation group: 'org.rauschig', name: 'jarchivelib', version: '1.2.0'

import org.rauschig.jarchivelib.ArchiveFormat;
import org.rauschig.jarchivelib.Archiver;
import org.rauschig.jarchivelib.ArchiverFactory;
import org.rauschig.jarchivelib.CompressionType;

  public static void unzip(File zipFile, File targetDirectory) throws IOException, IllegalAccessException {
    Archiver archiver = ArchiverFactory.createArchiver(ArchiveFormat.ZIP);
    archiver.extract(zipFile, targetDirectory);
  }

  public static void unTarGz(File tarFile, File targetDirectory) throws IOException {
    Archiver archiver = ArchiverFactory.createArchiver(ArchiveFormat.TAR, CompressionType.GZIP);
    archiver.extract(tarFile, targetDirectory);
  }

The other libraries get too complex for this simple task. That's why I love this library - 2 lines, done.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Valentin Michalak
Solution 2 dzikoysk
Solution 3
Solution 4 Kumar Sambhav
Solution 5 ofer borshtein
Solution 6
Solution 7 Lavandysh
Solution 8 svarog
Solution 9 Nicholas DiPiazza