Is there any sample code, how to practically 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?
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.
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.
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();
}
}
}
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();
org.apache.ant.compress.taskdefs.Unzip
don't have the method setSrc
, setDest
or execute
. –
Karame 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();
}
}
}
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();
}
}
}
}
}
}
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);
}
}
}
}
Pastebin
etc. –
Jewelfish 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.
An alternative of the very nice answer, still ZIP-slip vulnerability free, library-free, and Java 7+ variant but based on streams:
public static void unzip(File zipFile, Path targetDir) throws IOException {
// Normalize the path to get rid parent folder accesses
Path targetRoot = targetDir.normalize();
// Open the zip file as a FileSystem
try (FileSystem fs = FileSystems.newFileSystem(URI.create("jar:" + zipFile.toURI()), Map.of())) {
for (Path root : fs.getRootDirectories()) {
try (Stream<Path> walk = Files.walk(root)) {
// For each path in the zip
walk.forEach(
source -> {
// Compute the target path of the file in the destination folder
Path target = targetRoot.resolve(root.relativize(source).toString()).normalize();
if (target.startsWith(targetRoot)) {
// Only copy safe files
// see: https://snyk.io/research/zip-slip-vulnerability
try {
if (Files.isDirectory(source)) {
// Create folders
Files.createDirectories(target);
} else {
// Copy the file to the destination
Files.copy(source, target);
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
});
}
}
}
}
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.
Based on petrs's answer, here's a kotlin version, that I am now using:
fun ZipInputStream.extractTo(target: File) = use { zip ->
var entry: ZipEntry
while (zip.nextEntry.also { entry = it ?: return } != null) {
val file = File(target, entry.name)
if (entry.isDirectory) {
file.mkdirs()
} else {
file.parentFile.mkdirs()
zip.copyTo(file.outputStream())
}
}
}
© 2022 - 2024 — McMap. All rights reserved.