ZipHelper
ZipHelper creates ZIP archives. Construct it with a directory, a single File, or an array of files, then call
zip(). ZIP extraction is provided separately by UnzipHelper.
import com.ajaxjs.util.io.ZipHelper;
ZipHelper backup = new ZipHelper("C:/data", "C:/backup.zip");
backup.zip();
Sources and compression mode
Directory sources are traversed recursively; relative paths and empty directories are preserved. Symbolic links and non-regular files are rejected. The destination ZIP must be outside the source directory.
ZipHelper single = new ZipHelper(new File("report.csv"), "report.zip");
single.zip();
ZipHelper files = new ZipHelper(new File[]{new File("a.txt"), new File("b.txt")}, "files.zip");
files.setUseStore(true); // STORED: no compression
files.zip();
The default mode is DEFLATED. setUseStore(true) selects STORED; it calculates each source file's size and CRC32
before writing, so source files must not change during the operation. Multiple files must have distinct file names,
because they are stored at the archive root.
The archive is first written to a temporary sibling file, then replaces the target only after a successful write.
I/O failures are reported as UncheckedIOException.
Archive layout examples
Directory input is represented relative to the selected source directory, rather than including the source directory
name itself. All entry separators are /, including when the archive is created on Windows.
| Source | Entry in archive |
|---|---|
C:/work/source/root.txt |
root.txt |
C:/work/source/a/b/nested.txt |
a/b/nested.txt |
C:/work/source/empty |
empty/ |
new File[]{a.txt, b.txt} |
a.txt, b.txt |
Path source = Paths.get("C:/work/source");
new ZipHelper(source.toString(), "C:/out/source.zip").zip();
// Creates a root-level archive of the two files; duplicate base names are rejected.
new ZipHelper(new File[]{new File("logs/app.log"), new File("report.csv")},
"C:/out/files.zip").zip();
The directory walker records directory entries explicitly, so an empty directory remains visible after extraction. This matters for application templates and backup archives where directory structure is meaningful.
Compression choice
DEFLATED is the normal choice: the ZIP writer compresses while it writes. STORED writes raw file bytes; ZIP
requires the exact size and CRC32 in the entry header before those bytes are written. ZipHelper therefore reads each
file once to calculate metadata and again to archive it.
Use STORED only when no compression is desired (for example, an already-compressed media file and a compatible
consumer). Do not modify input files between those two passes. A changed file can make the recorded CRC or length
inconsistent with the archived bytes.
Safety and failure behavior
The helper intentionally rejects symbolic links and non-regular files, avoiding accidental inclusion of a file outside the selected tree. It also rejects a destination placed within the source tree, which would otherwise risk recursively adding the archive to itself. The destination parent directory is created when needed.
Writing to a temporary sibling archive before replacing the destination reduces the chance that a failed operation
leaves a half-written target. It is not a transaction across every filesystem: a process crash or an unsupported atomic
move can still leave normal filesystem-level recovery considerations. Handle UncheckedIOException when the caller
needs to surface or retry a failed backup.
Implementation notes
ZipHelper is a creation-only API; it does not make extraction safe by itself. When consuming untrusted ZIP files,
use UnzipHelper and keep its destination validation in place to defend against path
traversal entries such as ../../outside.txt.