package com.loopperfect.buckaroo.io; import com.google.common.base.Preconditions; import com.loopperfect.buckaroo.Either; import java.io.*; import java.nio.channels.ByteChannel; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileAttribute; import java.util.Optional; import java.util.Set; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public final class Files { private Files() { super(); } /* * Unzips a zip file at the given path into the given target path. * Optionally, a sub-path can be supplied which can be used to change the "root" of the zip file. * * For example: * * source: myZip.zip * target: myZip * subPath: stuff * * Input: * * +--+ myZip.zip * +--- file.txt * +--+ stuff * +--- hello.txt * +--+ a * +--- b.txt * * Output: * * +--+ myZip.zip * +--- file.txt * +--+ stuff * +--- hello.txt * +--+ a * +--- b.txt * +--+ myZip * +--- hello.txt * +--+ a * +--- b.txt * * Note how file.txt is not extracted because it is outside the sub-path, and hello.txt is at the root * of the target directory. * */ public static Optional<IOException> unzip(final Path source, final Path target, final Optional<Path> subPath) { final File targetDirectory = target.toFile(); if (!targetDirectory.exists()) { targetDirectory.mkdir(); } try (final ZipInputStream zipIn = new ZipInputStream(new FileInputStream(source.toFile()))) { ZipEntry entry = zipIn.getNextEntry(); while (entry != null) { final ZipEntry current = entry; // Check that the current entry lives in the sub-path (or there is no sub-path!) if (subPath.map(x -> current.getName().startsWith(x.toString())).orElse(true)) { final Path filePath = Paths.get( targetDirectory.toPath().toString(), current.getName().substring(subPath.map(x -> x.toString().length()).orElse(0))); if (current.isDirectory()) { File dir = filePath.toFile(); dir.mkdir(); } else { extractFile(zipIn, filePath); } } zipIn.closeEntry(); entry = zipIn.getNextEntry(); } } catch (final IOException e) { return Optional.of(e); } return Optional.empty(); } private static final int BUFFER_SIZE = 4096; private static void extractFile(final ZipInputStream zipIn, final Path filePath) throws IOException { try (final BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath.toFile()))) { byte[] bytesIn = new byte[BUFFER_SIZE]; int read = 0; while ((read = zipIn.read(bytesIn)) != -1) { bos.write(bytesIn, 0, read); } } } public static Either<IOException, ByteChannel> openByteChannel( final Path path, Set<? extends OpenOption > options, FileAttribute<?>... attrs) { Preconditions.checkNotNull(path); try { final ByteChannel channel = path.getFileSystem() .provider() .newByteChannel(path, options, attrs); return Either.right(channel); } catch (final IOException e) { return Either.left(e); } } }