java.util.zip
| Language | Java |
|---|---|
| License | GPL v2 with Classpath Exception (part of OpenJDK) |
| Repository | github.com/openjdk/jdk |
| Documentation | java.util.zip package, ZIP file system provider |
| Depends on | nothing (built into the JDK) |
| Read / write | yes / yes, ZIP64, stored and DEFLATE; no encryption |
The JDK has three ways to work with ZIP files:
ZipFile: random access to an existing archive through its central directory. Fast for listing and reading single entries.ZipInputStream/ZipOutputStream: forward-only streams for reading and writing, entry by entry. They work on anyInputStream/OutputStream.- the ZIP file system provider (
jdk.zipfs): opens a ZIP as ajava.nio.file.FileSystem, so you can useFiles.copy,Files.walkandFiles.deleteinside an archive, including modifying it.
All support ZIP64 and UTF-8 names (the default charset for names is UTF-8; pass another Charset for old archives). There’s no support for encryption or compression methods other than stored and DEFLATE. JAR files are read with the same classes (java.util.jar extends them).
Installation#
Nothing to install: part of every JDK.
Basic usage: list and extract a ZIP file#
Java
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 Unzip {
public static void main(String[] args) throws IOException {
Path target = Path.of("out").toAbsolutePath().normalize();
try (ZipFile zip = new ZipFile("in.zip")) {
Enumeration<? extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
System.out.printf("%-40s %10d -> %10d%n", entry.getName(), entry.getSize(), entry.getCompressedSize());
Path dest = target.resolve(entry.getName()).normalize();
if (!dest.startsWith(target)) { // Zip Slip protection
throw new IOException("bad entry name: " + entry.getName());
}
if (entry.isDirectory()) {
Files.createDirectories(dest);
} else {
Files.createDirectories(dest.getParent());
try (InputStream in = zip.getInputStream(entry)) {
Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
}
}
How to create a ZIP file from a folder#
Java
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.*;
import java.util.stream.Stream;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public static void zipFolder(Path folder, Path zipPath) throws IOException {
try (OutputStream os = Files.newOutputStream(zipPath);
ZipOutputStream zos = new ZipOutputStream(os);
Stream<Path> files = Files.walk(folder)) {
zos.setLevel(Deflater.BEST_COMPRESSION);
for (Path p : (Iterable<Path>) files.filter(Files::isRegularFile)::iterator) {
// entry names always use '/', relative to the folder
String name = folder.relativize(p).toString().replace('\\', '/');
zos.putNextEntry(new ZipEntry(name));
Files.copy(p, zos);
zos.closeEntry();
}
}
}
How to read one file from a ZIP#
Java
try (ZipFile zip = new ZipFile("in.zip")) {
ZipEntry entry = zip.getEntry("docs/readme.txt");
if (entry != null) {
try (InputStream in = zip.getInputStream(entry)) {
String text = new String(in.readAllBytes(), StandardCharsets.UTF_8);
System.out.println(text);
}
}
}
How to add a file to an existing ZIP (ZIP file system)#
Java
import java.net.URI;
import java.nio.file.*;
import java.util.Map;
Path zipPath = Path.of("existing.zip");
try (FileSystem fs = FileSystems.newFileSystem(zipPath, Map.of("create", "true"))) {
Files.copy(Path.of("notes.txt"), fs.getPath("/notes.txt"), StandardCopyOption.REPLACE_EXISTING);
Files.writeString(fs.getPath("/hello.txt"), "hello from zipfs");
Files.deleteIfExists(fs.getPath("/old.log"));
} // the archive is rewritten on close
"create", "true" creates the archive if it doesn’t exist.
How to read a ZIP from a stream#
Java
try (ZipInputStream zis = new ZipInputStream(inputStream)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
System.out.println(entry.getName());
byte[] data = zis.readAllBytes(); // reads only the current entry
}
}
Tips#
- For archives with names in an old DOS code page, pass a charset:
new ZipFile(file, Charset.forName("IBM437")). - To store an entry without compression with
ZipOutputStream, setentry.setMethod(ZipEntry.STORED)and set its size, compressed size and CRC (fromjava.util.zip.CRC32) beforeputNextEntry. ZipFilethrowsZipExceptionfor encrypted entries. Use zip4j for password-protected archives.