Apache Commons Compress
| Language | Java (8+) |
|---|---|
| License | Apache 2.0 |
| Repository | github.com/apache/commons-compress |
| Documentation | ZIP package guide, user guide |
| Depends on | Commons IO, Commons Lang; optional zstd-jni (Zstandard), XZ for Java |
| Read / write | yes / yes, ZIP64, stored and DEFLATE, Zstandard (1.28+); reads bzip2, Deflate64, Shrink, Implode; no encryption |
Commons Compress handles many archive formats (ZIP, tar, 7z, ar, arj, cpio, dump) and compressors (gzip, bzip2, xz, Zstandard, LZ4, Brotli). Its ZIP support goes beyond java.util.zip:
- reads and writes Unix permissions and symlinks (
getUnixMode,setUnixMode), and other extra fields - reads more compression methods: bzip2, Deflate64, and the old Shrink and Implode
- writes split archives and creates archives in parallel (
ParallelScatterZipCreator) - controls file name encoding and Unicode extra fields
ZipArchiveOutputStreamon a file can store entries of unknown size without compression
It can’t read or write encrypted entries; canReadEntryData tells you if an entry is encrypted or uses an unsupported method.
Installation#
Maven:
XML
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.28.0</version>
</dependency>
Gradle:
implementation("org.apache.commons:commons-compress:1.28.0")
Basic usage: extract a ZIP file#
Java
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.*;
import java.util.Enumeration;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
public class Unzip {
public static void main(String[] args) throws IOException {
Path target = Path.of("out").toAbsolutePath().normalize();
try (ZipFile zip = ZipFile.builder().setFile("in.zip").get()) {
Enumeration<ZipArchiveEntry> entries = zip.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
Path dest = target.resolve(entry.getName()).normalize();
if (!dest.startsWith(target)) throw new IOException("bad entry: " + entry.getName());
if (!zip.canReadEntryData(entry)) {
System.err.println("skipping encrypted or unsupported entry " + entry.getName());
continue;
}
if (entry.isDirectory()) {
Files.createDirectories(dest);
continue;
}
Files.createDirectories(dest.getParent());
try (InputStream in = zip.getInputStream(entry)) {
Files.copy(in, dest, StandardCopyOption.REPLACE_EXISTING);
}
System.out.printf("%s mode %o%n", entry.getName(), entry.getUnixMode());
}
}
}
}
How to create a ZIP file with Unix permissions#
Java
import java.io.IOException;
import java.nio.file.*;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
public static void writeZip() throws IOException {
try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(Path.of("out.zip"))) {
zos.setLevel(9);
zos.setUseZip64(Zip64Mode.AsNeeded); // the default
ZipArchiveEntry script = new ZipArchiveEntry(Path.of("run.sh"), "bin/run.sh");
script.setUnixMode(0100755); // regular file, rwxr-xr-x
zos.putArchiveEntry(script);
Files.copy(Path.of("run.sh"), zos);
zos.closeArchiveEntry();
zos.finish();
}
}
How to write a Zstandard-compressed entry#
Needs the com.github.luben:zstd-jni dependency:
Java
import org.apache.commons.compress.archivers.zip.ZipMethod;
ZipArchiveEntry entry = new ZipArchiveEntry("data.csv");
entry.setMethod(ZipMethod.ZSTD.getCode()); // method 93
zos.putArchiveEntry(entry);
Files.copy(Path.of("data.csv"), zos);
zos.closeArchiveEntry();
Only some tools read Zstandard ZIP entries (7-Zip, WinZip, recent libzip); use DEFLATE for archives you share.
How to create a split ZIP archive#
Java
long partSize = 100L * 1024 * 1024; // 100 MB parts: out.z01, out.z02, ..., out.zip
try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(Path.of("out.zip"), partSize)) {
ZipArchiveEntry entry = new ZipArchiveEntry(Path.of("big.iso"), "big.iso");
zos.putArchiveEntry(entry);
Files.copy(Path.of("big.iso"), zos);
zos.closeArchiveEntry();
}
To read a split archive, open the parts with ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(new File("out.zip")) and pass the channel to ZipFile.builder().setSeekableByteChannel(...).
Tips#
ZipFile.builder().setCharset("IBM437")reads names from old DOS archives without the UTF-8 flag.ZipArchiveInputStreamreads archives from streams but can’t see data only stored in the central directory (like Unix permissions); useZipFilewhen you have a file.getEntriesInPhysicalOrder()returns entries in the order they appear in the file, which is faster for reading all of them.