package cz.cuni.mff.d3s.been.util;
import java.io.*;
import java.util.Collection;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility facade designed to help with packing and unpacking of ZIP files.
*
* @author Tadeas Palusga
*
*/
public final class ZipUtil {
private static final Logger log = LoggerFactory.getLogger(ZipUtil.class);
/**
* Main method of this util. Creates ZIP archive from specified files in
* specified output file.
*
* @param items
* files to archive
* @param outputFile
* archive output file
* @throws IOException
* when some of specified files is missing, cannot be read or output
* file cannot be opened with WRITE privileges.
*/
public static void createZip(Collection<ItemToArchive> items, File outputFile) throws IOException {
final ZipArchiveOutputStream zaos = new ZipArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile)));
addFilesToZip(items, zaos);
zaos.finish();
zaos.close();
}
private static void addFilesToZip(Collection<ItemToArchive> items, ZipArchiveOutputStream zar) throws IOException {
for (ItemToArchive item : items) {
final String fixedName = (item.isDirectory()) ? nameWithSlash(item.getPathInZip())
: nameWithoutSlash(item.getPathInZip());
final ZipArchiveEntry zae = new ZipArchiveEntry(fixedName);
zae.setSize(item.getSize());
zar.putArchiveEntry(zae);
if (!item.isDirectory()) {
try (InputStream in = item.getInputStream()) {
IOUtils.copy(in, zar);
} catch (IOException e) {
throw e;
}
}
zar.closeArchiveEntry();
}
}
/**
* Unzips a file to specified directory.
*
* @param zipFile
* file to unzip
* @param toDir
* where to unzip
* @throws IOException
* when the file cannot be unzipped to the directory
*/
public static void unzipToDir(File zipFile, File toDir) throws IOException {
try (FileInputStream fis = new FileInputStream(zipFile)) {
unzipToDir(fis, toDir);
}
}
/**
*
* Unzips content of a stream to specified directory.
*
* @param is
* stream to unzip
* @param toDir
* where to unzip
* @throws IOException
* when the content of the stream cannot be unzipped to specified
* directory
*/
public static void unzipToDir(InputStream is, File toDir) throws IOException {
try (ZipArchiveInputStream zipStream = new ZipArchiveInputStream(is)) {
final long maxEntrySize = Integer.MAX_VALUE;
while (true) {
final ArchiveEntry entry = zipStream.getNextEntry();
if (entry == null) {
break;
}
if (!zipStream.canReadEntryData(entry)) {
log.warn("Can't read entry \"{}\" from stream, skipping it", entry);
continue;
}
final long entrySize = entry.getSize();
if (entrySize > maxEntrySize) {
log.error("Entry {} is too big", entry.toString());
continue;
}
final File f = new File(toDir.getAbsolutePath() + File.separator + entry.getName());
if (entry.isDirectory()) {
f.mkdirs();
} else {
f.getParentFile().mkdirs();
f.createNewFile();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
if (entrySize == -1) {
log.debug("Size of entry {} is unknown", entry);
}
IOUtils.copy(zipStream, fos);
log.debug("Copied ZIP entry \"{}\"", entry.toString());
} catch (IOException e) {
log.error("Problem copying ZIP entry \"{}\"", entry.toString(), e);
} finally {
try {
fos.close();
} catch (IOException e) {
log.error("Cannot close write destination file \"{}\"", f.getAbsolutePath());
}
}
}
}
zipStream.close();
}
}
private static final String nameWithSlash(String name) {
return (name.endsWith("/")) ? name : name + "/";
}
private static final String nameWithoutSlash(String name) {
return (name.endsWith("/")) ? name.substring(0, name.length() - 1) : name;
}
}