zt-zip: Practical ZIP Operations for Java Applications
ZeroTurnaround ZIP Library
At a glance
- What is it?
- ZeroTurnaround's zt-zip wraps java.util.zip with filesystem-oriented helpers for packing, unpacking, inspecting, and modifying ZIP archives. It suits Java applications that need concise archive operations, but it does not cap archive expansion or protect against resource-exhaustion attacks by itself.
- Who is it for?
- Adopt zt-zip when a Java application needs a compact filesystem layer over java.util.zip and can control access to uploaded archives. Do not adopt it as an isolated extractor for untrusted content, because its lack of decompressed-size and entry-count limits leaves resource controls to the caller.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why zt-zip Exists for Java ZIP Handling
zt-zip addresses a practical gap between `java.util.zip` and application code that repeatedly needs to move files between ZIP archives and directories. Its `ZipUtil` class supplies filesystem-oriented convenience methods for recursive packing and unpacking, entry selection, renaming, iteration, replacement, transformation, and archive comparison. The README says the project began while Rein Raudjärv was processing a large collection of large ZIP archives for LiveRebel internals.
The intended audience is Java developers who want these operations without assembling the underlying stream and archive plumbing for every use case. The design also reflects several deliberate constraints: reuse existing Java APIs, keep calls concise, avoid scanning an entire archive when one entry is needed, and avoid exposing unchecked streams that could keep files locked. Those goals make it more relevant to application code than to a general archive desktop application.
This is a library rather than a command-line tool or standalone service. The repository contains a Java source tree, Gradle build files, and Maven publication metadata, but the README does not document a CLI. Consumers are expected to add the published JAR to an existing Java or JVM-based project.
How ZipUtil Handles Archives and Streams
The documented implementation is built on the stream-based APIs in `java.util.zip`. `ZipUtil` exposes separate operations for work that does not require loading a complete entry, such as packing a directory and extracting an archive. It also offers methods for direct filesystem changes, including converting an archive into a directory with `explode` and converting a directory into an archive with `unexplode`.
Entry filtering and path rewriting are handled through `NameMapper`. Its `map` method receives an entry name and returns either the desired output name or `null` to disregard that entry. Returning the original name selects an entry, while stripping a prefix changes its location during extraction. This callback mechanism covers inclusion, exclusion, and renaming without requiring a separate rule engine.
For inspection, the README distinguishes between `ZipInfoCallback`, which receives `ZipEntry` metadata, and `ZipEntryCallback`, which also receives an `InputStream`. The latter supports examining entry content without first converting the whole entry into a byte array. The project's stated goal of avoiding a complete traversal when only one entry is needed is reflected by operations such as `containsEntry` and targeted `unpackEntry` calls.
Stream safety is part of the library's stated design intent. The non-functional requirements specifically call for avoiding open streams and locked files, but the README does not document every internal resource-management rule or guarantee beyond that stated intent.
Maven and Gradle Setup for zt-zip 1.18.2
The published artifact is `org.zeroturnaround:zt-zip`. The README points to Maven Central and gives version `1.18.2` for both Maven and Gradle. In Maven, add the dependency inside the project's existing `<dependencies>` section:
<dependency>
<groupId>org.zeroturnaround</groupId>
<artifactId>zt-zip</artifactId>
<version>1.18.2</version>
<type>jar</type>
</dependency>For a Gradle project written in Kotlin, declare the implementation dependency in the relevant build script:
implementation("org.zeroturnaround:zt-zip:1.18.2")Current releases require Java 8 or later at runtime. The compatibility history is explicit: `1.8` is the last release compatible with Java 1.4, releases from `1.9` require Java 5 at the source level, and current releases require Java 8 or later at runtime. Applications that cannot move to Java 8 therefore need an older release rather than `1.18.2`.
The README also provides a ProGuard configuration because library classes may trigger warnings from optional or indirect SLF4J references. Add the documented option to the ProGuard configuration when that shrinker is in use:
-dontwarn org.slf4j.**The repository includes `gradlew` and `gradlew.bat`, but the README does not give a project-build tutorial, so no build command is inferred here.
First Real Use: Inspecting and Extracting a ZIP Entry
A small first use can check for one entry and extract only that entry. Here, `containsEntry` asks whether `foo.txt` exists in `/tmp/demo.zip`:
boolean exists = ZipUtil.containsEntry(new File("/tmp/demo.zip"), "foo.txt");After that check succeeds, the targeted extraction call reads the named entry. This variant loads the entry into a byte array, so it is intended for an entry whose size the application understands:
byte[] bytes = ZipUtil.unpackEntry(
new File("/tmp/demo.zip"),
"foo.txt"
);To write that entry directly to disk instead, use the filesystem overload:
ZipUtil.unpackEntry(
new File("/tmp/demo.zip"),
"foo.txt",
new File("/tmp/bar.txt")
);The expected filesystem effect is the creation of `/tmp/bar.txt` from `foo.txt`. The README also supports a specific character set through an overload such as:
byte[] bytes = ZipUtil.unpackEntry(
new File("/tmp/demo.zip"),
"foo.txt",
Charset.forName("IBM437")
);For an entire archive, the shorter call extracts its contents below `/tmp/demo`:
ZipUtil.unpack(new File("/tmp/demo.zip"), new File("/tmp/demo"));Unlike the byte-array call, the README states that `unpack` streams entries to disk. That distinction matters for large entries, but it does not create archive-wide resource limits.
Packing and Selective Extraction Through NameMapper
Directory-to-archive conversion is also a single call. The following example compresses `/tmp/demo` into `/tmp/demo.zip`:
ZipUtil.pack(new File("/tmp/demo"), new File("/tmp/demo.zip"));For the inverse filesystem operation, `unexplode` turns a directory into a ZIP file. The README presents it as compressing a directory which becomes a ZIP archive, with the archive and directory paths supplied to the operation:
ZipUtil.unexplode(new File("/tmp/demo.zip"));Selective extraction relies on the `NameMapper` contract. The callback can select entries beginning with `doc/`, preserving that directory in the output:
ZipUtil.unpack(new File("/tmp/demo.zip"), new File("/tmp/demo"), new NameMapper() {
public String map(String name) {
return name.startsWith("doc/") ? name : null;
}
});A mapper can also remove a selected directory prefix, placing its contents directly at the extraction root:
final String prefix = "doc/";
ZipUtil.unpack(new File("/tmp/demo.zip"), new File("/tmp/demo"), new NameMapper() {
public String map(String name) {
return name.startsWith(prefix) ? name.substring(prefix.length()) : name;
}
});The mapper is flexible, but its predicates become application policy. zt-zip supplies the naming mechanism, while the caller decides which files are acceptable.
Path Traversal Protection and Zip Bomb Exposure
zt-zip does address one specific archive security problem. During unpacking, it rejects an entry whose path resolves outside the target directory and throws `MaliciousZipException`. This prevents the directory traversal pattern commonly called Zip Slip. An entry that resolves to the target directory itself is treated as a no-op, including when its name is `/`, so its stored permissions are not applied to that directory.
That protection does not make arbitrary archive extraction safe by default. The library imposes no limit on decompressed size, compression ratio, or entry count. The README explicitly warns that an untrusted archive can exhaust memory or disk through a zip bomb. `unpack` streams entries to disk, but it never caps total bytes written or the number of entries. `unpackEntry` is more exposed to memory pressure because it loads an entire entry into memory at once.
This is the clearest wrong-tool case in the project. If incoming archives can be hostile and the application has no independent validation layer, `ZipUtil.unpack` alone is insufficient. The caller must cap entry count, individual entry size, and total uncompressed size, and should favor streaming for large or untrusted entries. The README gives these as application responsibilities rather than built-in `ZipUtil` options.
The distinction is important: zt-zip rejects paths that escape the destination, but it does not decide whether an archive is too expensive to process.
Maintenance, Compatibility, and Apache-2.0 Obligations
The repository is not archived. Its last push was on 2026-10-13, and the listed releases include `v1.18.2` on 2026-07-03, `v1.18.1` on 2026-07-01, and `v1.18.0` on 2026-07-01. Those dates indicate recent repository activity, although the README does not provide a formal support window or long-term-support policy.
Upgrade planning starts with the runtime boundary. Current releases require Java 8 or later, so adopting `1.18.2` may require changes outside the library if an application still targets an older JVM. The `1.8` compatibility endpoint is documented, but the README does not recommend that older version for new projects.
The project uses the Apache-2.0 license, and the repository contains `LICENSE` and `NOTICE` files. The presence of `NOTICE` means consumers should account for any applicable notice requirements when redistributing the library. This is a packaging and compliance check, not legal advice about a particular distribution.
Repository activity alone does not tell consumers whether upgrading will require source changes. The `CHANGELOG.md` file is present in the repository, but its contents are not provided here, so no specific migration behavior can be confirmed.
Editorial conclusion
Adopt zt-zip when a Java application needs a compact filesystem layer over java.util.zip and can control access to uploaded archives. Do not adopt it as an isolated extractor for untrusted content, because its lack of decompressed-size and entry-count limits leaves resource controls to the caller. Check the required Java version first, then verify archive filtering, replacement behavior, and application-specific expansion limits before integrating `ZipUtil` into a production workflow.
Frequently asked questions
Which Java version is required by the current zt-zip release?
The README states that current releases require Java 8 or later at runtime. It also identifies `1.8` as the last Java 1.4-compatible release.
Does zt-zip protect against Zip Slip and zip bomb attacks?
zt-zip rejects paths that resolve outside the extraction target by throwing `MaliciousZipException`. It does not cap decompressed size, compression ratio, or entry count, so applications must enforce their own limits for untrusted archives.
How do I include or rename ZIP entries with zt-zip?
Pass a `NameMapper` to methods such as `unpack` or `pack`. Return a transformed name to include or rename an entry, and return `null` to disregard it.
Does zt-zip have a command-line interface?
The README documents the Java API through `ZipUtil` and Maven or Gradle dependencies. It does not document a CLI.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/zeroturnaround-zt-zip)Was this useful?