I'm working on an auto updater for a game client and I've run into an issue.
What I need it to do: Download cache.zip and client.zip. Extract cache.zip to cacheDir and extract client.zip to the same location they are running the jar(game).
What it does right now: Downloads both cache.zip and client.zip. Extracts cache.zip to the correct location, but also to the location the jar is at. It doesn't extract client.zip at all.
I use this function to unzip a file:
public static void unzip(final ZipFile source, final File destination) throws IOException {
for (final ZipEntry entry : Collections.list(source.entries())) {
unzip(source, entry, destination);
}
}
private static void unzip(final ZipFile source, final ZipEntry entry, final File destination) throws IOException {
if (!entry.isDirectory()) {
final File resource = new File(destination, entry.getName());
if (!resource.getCanonicalPath().startsWith(destination.getCanonicalPath() + File.separator)) {
throw new IOException("Entry is outside of the target dir: " + entry);
}
final File folder = resource.getParentFile();
if (!folder.exists()) {
if (!folder.mkdirs()) {
throw new IOException();
}
}
try (final BufferedInputStream input = new BufferedInputStream(source.getInputStream(entry));
final BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(resource))) {
output.write(input.readAllBytes());
output.flush();
}
}
}
Here are some overload:
public static void unzip(final String file) throws IOException {
final File source = new File(file);
unzip(
new ZipFile(source),
new File(source.getParent(), source.getName().substring(0, source.getName().lastIndexOf('.')))
);
}
public static void unzip(final String source, final String destination) throws IOException {
unzip(new File(source), new File(destination));
}
public static void unzip(final File source, final File destination) throws IOException {
unzip(new ZipFile(source), destination);
}
Note:
It ignore the empty directories. You can add an else and mkdir the entries if you need them.
If you need to speedup the process, you can add a threadpool on unzip(final ZipFile source, final File destination)
for calling unzip(final ZipFile source, final ZipEntry entry, final File destination)
with multiple threads.
unzip(final ZipFile source, final ZipEntry entry, final File destination)
is checking for each entry if the canonical path of the output start by the canonical path of the destination to avoid Zip Slip Vulnerability
- See https://snyk.io/research/zip-slip-vulnerability - but you may ignore this check for you use case.
And if you need to get your jar repertory:
String fileRepertory = Setup.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath();
And finally, if you need to zip a file (I post it here as a backup for myself ^^')
public static void zip(final File destination, final List<File> toZip) throws IOException {
try (final ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(destination))) {
for (final File file : toZip) {
final ZipEntry entry = new ZipEntry(file.getCanonicalPath());
zip.putNextEntry(entry);
zip.write(Files.readAllBytes(file.toPath()));
}
}
}