/*
* Copyright (c) 2014 EMC Corporation
* All Rights Reserved
*/
package com.emc.storageos.management.backup.util;
import com.google.common.base.Preconditions;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
public class ZipUtil {
private static final Logger log = LoggerFactory.getLogger(ZipUtil.class);
// Defines separator for ZipEntry, because ZipEntry determine directory by result of method "endsWith("/")"
private static final String SEPARATOR = "/";
private static final int DELAY_THRESHOLD_IN_SECOND = 5;
private ZipUtil() {
}
/**
* Compresses the given directory and all its sub-directories into a ZIP file with
* default compress level.
* <p>
* The ZIP file must not be a directory.
*
* @param sourceDir
* source directory.
* @param targetZip
* ZIP file that will be created or overwritten.
*/
public static void pack(final File sourceDir, final File targetZip) throws IOException {
pack(sourceDir, targetZip, Deflater.DEFAULT_COMPRESSION);
}
/**
* Compresses the given directory and all its sub-directories into a ZIP file with specified
* compress level.
* <p>
* The ZIP file must not be a directory.
*
* @param sourceDir
* source directory.
* @param targetZip
* ZIP file that will be created or overwritten.
*/
public static void pack(final File sourceDir, final File targetZip, final int compressionLevel)
throws IOException {
Preconditions.checkArgument(sourceDir != null
&& sourceDir.exists()
&& sourceDir.isDirectory(),
"Source directory is not exist: %s", sourceDir.getAbsolutePath());
long startTime = System.currentTimeMillis();
ZipOutputStream zipOut = null;
try {
zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(targetZip)));
zipOut.setLevel(compressionLevel);
pack(sourceDir, sourceDir.getParent() + File.separator, zipOut);
} finally {
IOUtils.closeQuietly(zipOut);
long interval = (System.currentTimeMillis() - startTime) / 1000;
if (interval >= DELAY_THRESHOLD_IN_SECOND) {
long folderSize = FileUtils.sizeOfDirectory(sourceDir);
log.info(String.format("Zip folder: %s from %d to %d bytes, took %s seconds",
sourceDir.getAbsolutePath(), folderSize, targetZip.length(), interval));
}
}
}
private static void pack(final File sourceDir,
final String prefixPath,
final ZipOutputStream zipOut)
throws IOException {
File[] files = sourceDir.listFiles();
if (files == null) {
return;
}
for (File file : files) {
String zipEntryName = file.getPath().replace(prefixPath, "");
log.debug("Packing file: {}", zipEntryName);
if (file.isDirectory()) {
zipEntryName += SEPARATOR;
}
ZipEntry zipEntry = new ZipEntry(zipEntryName);
zipOut.putNextEntry(zipEntry);
if (file.isFile()) {
zipEntry.setSize(file.length());
zipEntry.setTime(file.lastModified());
FileUtils.copyFile(file, zipOut);
}
zipOut.closeEntry();
if (file.isDirectory()) {
pack(file, prefixPath, zipOut);
}
}
}
/**
* Unpacks a ZIP file to the given directory.
* <p>
* The output directory must not be a file.
*
* @param sourceZip
* The instance of input ZIP file.
* @param targetDir
* output directory (created automatically if not found).
*/
public static void unpack(File sourceZip, final File targetDir) throws IOException {
ZipFile zipFile = null;
try {
zipFile = new ZipFile(sourceZip);
Enumeration<? extends ZipEntry> entriesEnum = zipFile.entries();
while (entriesEnum.hasMoreElements()) {
unpackEntry(zipFile, entriesEnum.nextElement(), targetDir);
}
} finally {
try {
if (zipFile != null) {
zipFile.close();
}
} catch (IOException e) {
// Just ignore it
}
}
}
private static void unpackEntry(ZipFile zipFile, ZipEntry zipEntry, File targetDir)
throws IOException {
InputStream is = null;
try {
log.debug("Unpacking file: {}", zipEntry.getName());
is = zipFile.getInputStream(zipEntry);
File file = new File(targetDir, zipEntry.getName());
if (zipEntry.isDirectory()) {
file.mkdirs();
} else {
FileUtils.copyInputStreamToFile(is, file);
}
file.setLastModified(zipEntry.getTime());
} finally {
IOUtils.closeQuietly(is);
}
}
}