
com.xceptance.common.util.zip.ZipUtils Maven / Gradle / Ivy
/*
* Copyright (c) 2005-2024 Xceptance Software Technologies GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.xceptance.common.util.zip;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.xceptance.common.util.ParameterCheckUtils;
import com.xceptance.xlt.common.XltConstants;
/**
* The ZipUtils class provides convenience methods for creating and unpacking ZIP archives.
*
* @author Jörg Werner (Xceptance Software Technologies GmbH)
*/
public final class ZipUtils
{
/**
* Default constructor. Declared private to prevent external instantiation.
*/
private ZipUtils()
{
}
/**
* Class logger.
*/
private static final Logger log = LoggerFactory.getLogger(ZipUtils.class);
/**
* Zips the given directory recursively to the specified file. If the file already exists, it will be overwritten,
* otherwise it will be created.
*
* @param directory
* the directory to zip
* @param zipFile
* the resulting zip file
* @throws java.io.IOException
* if an I/O error occurs
*/
public static void zipDirectory(final File directory, final File zipFile) throws IOException
{
zipDirectory(directory, null, zipFile);
}
/**
* Zips the given directory recursively to the specified file. If the file already exists, it will be overwritten,
* otherwise it will be created.
*
* @param directory
* the directory to zip
* @param fileFilter
* a file filter to choose a sub set of files
* @param zipFile
* the resulting zip file
* @throws java.io.IOException
* if an I/O error occurs
*/
public static void zipDirectory(final File directory, final FileFilter fileFilter, final File zipFile) throws IOException
{
zipDirectory(directory, fileFilter, zipFile, true);
}
/**
* Zips the given directory recursively to the specified file. If the file already exists, it will be overwritten,
* otherwise it will be created. Depending on the boolean argument the returned stream is closed. You should use
* {@link #zipDirectory(File, File)} is possible. This method is only for the case where you manually have to add
* entries to the returned stream after the argument directory has been zipped.
*
* @param directory
* the directory to zip
* @param fileFilter
* a file filter to choose a sub set of files
* @param zipFile
* the resulting zip file
* @throws java.io.IOException
* if an I/O error occurs
*/
public static ZipOutputStream zipDirectory(final File directory, final FileFilter fileFilter, final File zipFile,
final boolean closeStream)
throws IOException
{
return zipDirectory(directory, fileFilter, zipFile, new File("."), closeStream);
}
/**
* Zips the given directory recursively to the specified file. If the file already exists, it will be overwritten,
* otherwise it will be created. Depending on the boolean argument the returned stream is closed. You should use
* {@link #zipDirectory(File, File)} is possible. This method is only for the case where you manually have to add
* entries to the returned stream after the argument directory has been zipped.
*
* @param directory
* the directory to zip
* @param fileFilter
* a file filter to choose a sub set of files
* @param zipFile
* the resulting zip file
* @throws java.io.IOException
* if an I/O error occurs
*/
public static ZipOutputStream zipDirectory(final File directory, final FileFilter fileFilter, final File zipFile, final File relDir,
final boolean closeStream)
throws IOException
{
if (zipFile == null)
{
throw new IllegalArgumentException("The target file must not be null.");
}
// zip the directory tree
ZipOutputStream out = null;
try
{
out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
zipDirectory(out, directory, fileFilter, relDir);
}
finally
{
if (closeStream)
{
IOUtils.closeQuietly(out);
}
}
return out;
}
/**
* @param directory
* @throws IllegalArgumentException
*/
private static void checkDirectory(final File directory) throws IllegalArgumentException
{
if (directory == null)
{
throw new IllegalArgumentException("The directory parameter must not be null.");
}
if (!directory.isDirectory())
{
throw new IllegalArgumentException("Not a directory: " + directory);
}
}
/**
* Zips the argument directory to the argument stream. However uses the argument file filter to filter files in the
* argument directory and relocates files to the argument relative directory. Does NOT closes the argument stream!
*
* @param out
* the stream to which to write the contents, it is NOT closed by this method!
* @param directory
* the directory to zip
* @param fileFilter
* the filter to use, may be null
* @param relDir
* the relative directory to which to relocate the files, for example new File(".")
* @throws IllegalArgumentException
* if the argument directory is null
or is not a directory or the argument stream is
* null
*/
public static void zipDirectory(final ZipOutputStream out, final File directory, final FileFilter fileFilter, final File relDir)
throws IOException
{
// parameter check
checkDirectory(directory);
if (out == null)
{
throw new IllegalArgumentException("The target output stream must not be null!");
}
log.debug("Start zipping files");
addDir(directory, fileFilter, relDir, out);
log.debug("Finished zipping files");
out.flush();
}
/**
* Adds the given directory to the specified ZIP output stream. The directory will be stored as a relative path
* which is given by relDir
.
*
* @param dir
* the physical directory to zip
* @param fileFilter
* the file filter to be used
* @param relDir
* the relative directory in the ZIP file
* @param out
* the ZIP target stream
* @throws java.io.IOException
* if an I/O error occurs
*/
private static void addDir(final File dir, final FileFilter fileFilter, final File relDir, final ZipOutputStream out) throws IOException
{
final File[] files = dir.listFiles(fileFilter);
if (files != null)
{
for (final File file : files)
{
final File relFile = new File(relDir, file.getName());
// always use forward slashes -> this works across all OS
final String relFileName = relFile.toString().replace('\\', '/');
if (file.isDirectory())
{
addDirectoryEntry(out, relFileName);
// add the directory contents
addDir(file, fileFilter, relFile, out);
}
else
{
addRegularFile(out, file, relFileName);
}
}
}
}
/**
* Adds an entry for the directory with the argument name to the argument stream. Does not add the contents of the
* directory to the stream.
*
* @param out
* the stream to which to add the entry
* @param relFileName
* @throws IOException
*/
public static void addDirectoryEntry(final ZipOutputStream out, final String relFileName) throws IOException
{
log.debug("Adding directory to ZIP: " + relFileName);
// add a record for the directory itself
out.putNextEntry(new ZipEntry(relFileName + "/"));
out.closeEntry();
}
/**
* Adds a regular file to the argument stream.
*
* @param out
* the stream to which to add the entry
* @param file
* the file whose contents to add to the stream
* @param relFileName
* @throws FileNotFoundException
* @throws IOException
*/
public static void addRegularFile(final ZipOutputStream out, final File file, final String relFileName)
throws FileNotFoundException, IOException
{
log.debug("Adding file to ZIP: " + relFileName);
// add the file
out.putNextEntry(new ZipEntry(relFileName));
FileUtils.copyFile(file, out);
out.closeEntry();
}
/**
* Unzips the given ZIP file to the specified directory. If the directory does not exist yet, it will be created.
*
* @param zipFile
* the zip file
* @param directory
* the target directory
* @throws java.io.IOException
* if an I/O error occurs
*/
public static void unzipFile(final File zipFile, final File directory) throws IOException
{
unzipFile(zipFile, directory, false);
}
/**
* Unzips the given ZIP file to the specified directory. If the directory does not exist yet, it will be created.
* Depending on the parameter, timers files are stored to disk either in plain or in compressed form (gzipped).
*
* @param zipFile
* the zip file
* @param directory
* the target directory
* @param compressedTimerFiles
* do we want to keep the timers in a compressed form
* @throws java.io.IOException
* if an I/O error occurs
*/
public static void unzipFile(final File zipFile, final File directory, final boolean compressedTimerFiles) throws IOException
{
ParameterCheckUtils.isReadableFile(zipFile, "zipFile");
ParameterCheckUtils.isNotNull(directory, "directory");
// make sure the target directory is available
if (!directory.isDirectory())
{
directory.mkdirs();
}
// unzip the zip file
try (final ZipInputStream in = new ZipInputStream(new FileInputStream(zipFile)))
{
ZipEntry entry = null;
while ((entry = in.getNextEntry()) != null)
{
final File file = new File(directory, entry.getName());
if (entry.isDirectory())
{
FileUtils.forceMkdir(file);
}
else
{
// do we want to store the timers compressed
File compressedFile = null;
boolean compressIt = false;
// shall we compress timers?
if (compressedTimerFiles)
{
// we need the name of the file, without any path element
final String fileName = file.getName();
boolean b1 = XltConstants.TIMER_FILENAME_PATTERNS.stream().anyMatch(p -> p.asPredicate().test(fileName));
boolean b2 = XltConstants.CPT_TIMER_FILENAME_PATTERNS.stream().anyMatch(p -> p.asPredicate().test(fileName));
// one pattern matched
if (b1 || b2)
{
// determine the new name
compressedFile = new File(directory, entry.getName() + ".gz");
compressIt = true; // indicate the need for compression
}
}
try (final OutputStream out = compressIt ? new GZIPOutputStream(new FileOutputStream(compressedFile))
: new FileOutputStream(file))
{
// cannot use this as it DOES close the input stream
// FileUtils.copyToFile(in, file);
IOUtils.copy(in, out);
}
}
in.closeEntry();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy