Apache Commons Compress
| Language | Java 8+ |
|---|---|
| License | Apache 2.0 |
| Repository | github.com/apache/commons-compress |
| Documentation | 7z examples, SevenZFile javadoc, limitations |
| Depends on | commons-io, commons-lang3; XZ for Java (org.tukaani:xz) for LZMA and LZMA2 |
| Read / write | yes / yes; reads LZMA, LZMA2, BZip2, Deflate, Deflate64, Delta, BCJ filters, AES-256; writes LZMA2 (default), BZip2, Deflate, Copy without encryption |
Commons Compress is the Apache library for archive and compression formats in Java: ZIP, TAR, 7z, CPIO, AR, ARJ, DUMP and the compressors gzip, bzip2, xz, lzma, zstd, brotli and Snappy. Its 7z support is in the org.apache.commons.compress.archivers.sevenz package: SevenZFile reads, SevenZOutputFile writes.
Reading supports solid archives, compressed headers and AES-256 encrypted archives (data and headers). It doesn’t support PPMd or BCJ2, so some archives made by 7-Zip with -m0=PPMd or with default settings for .exe files (BCJ2 at high levels) can’t be read. Writing produces non-solid archives without encryption or header compression.
SevenZFile needs random access (a File, Path or SeekableByteChannel), because the 7z header is at the end of the file. It can’t read from an InputStream.
Installation#
Maven:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>org.tukaani</groupId>
<artifactId>xz</artifactId>
<version>1.10</version>
</dependency>
Gradle:
implementation("org.apache.commons:commons-compress:1.28.0")
implementation("org.tukaani:xz:1.10")
Basic usage: list and extract a 7z archive#
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class Extract7z {
public static void main(String[] args) throws IOException {
Path outDir = Path.of("output").toAbsolutePath().normalize();
try (SevenZFile sevenZFile = SevenZFile.builder().setFile("archive.7z").get()) {
SevenZArchiveEntry entry;
while ((entry = sevenZFile.getNextEntry()) != null) {
Path target = outDir.resolve(entry.getName()).normalize();
if (!target.startsWith(outDir)) {
throw new IOException("bad entry name: " + entry.getName()); // "../" in the name
}
if (entry.isDirectory()) {
Files.createDirectories(target);
continue;
}
System.out.println(entry.getName() + " " + entry.getSize() + " bytes");
Files.createDirectories(target.getParent());
try (InputStream in = sevenZFile.getInputStream(entry)) {
Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
}
Reading entries in order with getNextEntry is the fast path for solid archives: each block is decompressed once.
How to list files without extracting#
try (SevenZFile sevenZFile = SevenZFile.builder().setFile("archive.7z").get()) {
for (SevenZArchiveEntry entry : sevenZFile.getEntries()) {
System.out.printf("%s %d %s%n", entry.getName(), entry.getSize(),
entry.getHasLastModifiedDate() ? entry.getLastModifiedDate() : "");
}
}
How to open a password-protected 7z archive#
try (SevenZFile sevenZFile = SevenZFile.builder()
.setFile("secret.7z")
.setPassword("secret".toCharArray())
.get()) {
SevenZArchiveEntry entry;
while ((entry = sevenZFile.getNextEntry()) != null) {
byte[] content = sevenZFile.getInputStream(entry).readAllBytes();
// ...
}
}
A wrong password throws PasswordRequiredException when none was given, or usually an IOException with a CRC or LZMA data error.
How to create a 7z archive#
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZMethod;
import org.apache.commons.compress.archivers.sevenz.SevenZOutputFile;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
public class Create7z {
public static void main(String[] args) throws IOException {
try (SevenZOutputFile out = new SevenZOutputFile(new File("backup.7z"))) {
out.setContentCompression(SevenZMethod.LZMA2); // default; BZIP2, DEFLATE, COPY
// a file from disk
Path report = Path.of("report.pdf");
SevenZArchiveEntry entry = out.createArchiveEntry(report, "docs/report.pdf");
out.putArchiveEntry(entry);
out.write(report);
out.closeArchiveEntry();
// bytes from memory
byte[] data = "hello, world\n".getBytes(StandardCharsets.UTF_8);
SevenZArchiveEntry hello = new SevenZArchiveEntry();
hello.setName("hello.txt");
out.putArchiveEntry(hello);
out.write(data);
out.closeArchiveEntry();
} // close() writes the header at the end
}
}
How to read a 7z archive from a byte array#
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
byte[] bytes = Files.readAllBytes(Path.of("archive.7z")); // or from a download
try (SevenZFile sevenZFile = SevenZFile.builder()
.setSeekableByteChannel(new SeekableInMemoryByteChannel(bytes))
.get()) {
// ... getNextEntry() as above
}
Tips#
- Set
setMaxMemoryLimitKiBon the builder when you open untrusted archives: a crafted header or a huge LZMA dictionary can otherwise use a lot of memory. setTryToRecoverBrokenArchives(true)letsSevenZFileread archives whose end header is damaged or missing (for example an interrupted download).- Split archives (
.7z.001,.002): open them as one channel withMultiReadOnlySeekableByteChannel.forFiles(...).