com.dahuatech.hutool.core.util.ZipUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-sdk-common Show documentation
Show all versions of java-sdk-common Show documentation
Dahua ICC Open API SDK for Java
package com.dahuatech.hutool.core.util;
import com.dahuatech.hutool.core.exceptions.UtilException;
import com.dahuatech.hutool.core.io.FastByteArrayOutputStream;
import com.dahuatech.hutool.core.io.FileUtil;
import com.dahuatech.hutool.core.io.IORuntimeException;
import com.dahuatech.hutool.core.io.IoUtil;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.*;
/**
* 压缩工具类
*
* @author Looly
*/
public class ZipUtil {
private static final int DEFAULT_BYTE_ARRAY_LENGTH = 32;
/** 默认编码,使用平台相关编码 */
private static final Charset DEFAULT_CHARSET = CharsetUtil.defaultCharset();
/**
* 打包到当前目录,使用默认编码UTF-8
*
* @param srcPath 源文件路径
* @return 打包好的压缩文件
* @throws UtilException IO异常
*/
public static File zip(String srcPath) throws UtilException {
return zip(srcPath, DEFAULT_CHARSET);
}
/**
* 打包到当前目录
*
* @param srcPath 源文件路径
* @param charset 编码
* @return 打包好的压缩文件
* @throws UtilException IO异常
*/
public static File zip(String srcPath, Charset charset) throws UtilException {
return zip(FileUtil.file(srcPath), charset);
}
/**
* 打包到当前目录,使用默认编码UTF-8
*
* @param srcFile 源文件或目录
* @return 打包好的压缩文件
* @throws UtilException IO异常
*/
public static File zip(File srcFile) throws UtilException {
return zip(srcFile, DEFAULT_CHARSET);
}
/**
* 打包到当前目录
*
* @param srcFile 源文件或目录
* @param charset 编码
* @return 打包好的压缩文件
* @throws UtilException IO异常
*/
public static File zip(File srcFile, Charset charset) throws UtilException {
final File zipFile =
FileUtil.file(srcFile.getParentFile(), FileUtil.mainName(srcFile) + ".zip");
zip(zipFile, charset, false, srcFile);
return zipFile;
}
/**
* 对文件或文件目录进行压缩
* 不包含被打包目录
*
* @param srcPath 要压缩的源文件路径。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
* @param zipPath 压缩文件保存的路径,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @return 压缩好的Zip文件
* @throws UtilException IO异常
*/
public static File zip(String srcPath, String zipPath) throws UtilException {
return zip(srcPath, zipPath, false);
}
/**
* 对文件或文件目录进行压缩
*
* @param srcPath 要压缩的源文件路径。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
* @param zipPath 压缩文件保存的路径,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param withSrcDir 是否包含被打包目录
* @return 压缩文件
* @throws UtilException IO异常
*/
public static File zip(String srcPath, String zipPath, boolean withSrcDir) throws UtilException {
return zip(srcPath, zipPath, DEFAULT_CHARSET, withSrcDir);
}
/**
* 对文件或文件目录进行压缩
*
* @param srcPath 要压缩的源文件路径。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
* @param zipPath 压缩文件保存的路径,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param charset 编码
* @param withSrcDir 是否包含被打包目录
* @return 压缩文件
* @throws UtilException IO异常
*/
public static File zip(String srcPath, String zipPath, Charset charset, boolean withSrcDir)
throws UtilException {
final File srcFile = FileUtil.file(srcPath);
final File zipFile = FileUtil.file(zipPath);
zip(zipFile, charset, withSrcDir, srcFile);
return zipFile;
}
/**
* 对文件或文件目录进行压缩
* 使用默认UTF-8编码
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param withSrcDir 是否包含被打包目录,只针对压缩目录有效。若为false,则只压缩目录下的文件或目录,为true则将本目录也压缩
* @param srcFiles 要压缩的源文件或目录。
* @return 压缩文件
* @throws UtilException IO异常
*/
public static File zip(File zipFile, boolean withSrcDir, File... srcFiles) throws UtilException {
return zip(zipFile, DEFAULT_CHARSET, withSrcDir, srcFiles);
}
/**
* 对文件或文件目录进行压缩
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param charset 编码
* @param withSrcDir 是否包含被打包目录,只针对压缩目录有效。若为false,则只压缩目录下的文件或目录,为true则将本目录也压缩
* @param srcFiles 要压缩的源文件或目录。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
* @return 压缩文件
* @throws UtilException IO异常
*/
public static File zip(File zipFile, Charset charset, boolean withSrcDir, File... srcFiles)
throws UtilException {
return zip(zipFile, charset, withSrcDir, null, srcFiles);
}
/**
* 对文件或文件目录进行压缩
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param charset 编码
* @param withSrcDir 是否包含被打包目录,只针对压缩目录有效。若为false,则只压缩目录下的文件或目录,为true则将本目录也压缩
* @param filter 文件过滤器,通过实现此接口,自定义要过滤的文件(过滤掉哪些文件或文件夹不加入压缩)
* @param srcFiles 要压缩的源文件或目录。如果压缩一个文件,则为该文件的全路径;如果压缩一个目录,则为该目录的顶层目录路径
* @return 压缩文件
* @throws UtilException IO异常
* @since 4.6.5
*/
public static File zip(
File zipFile, Charset charset, boolean withSrcDir, FileFilter filter, File... srcFiles)
throws UtilException {
validateFiles(zipFile, srcFiles);
try (ZipOutputStream out = getZipOutputStream(zipFile, charset)) {
String srcRootDir;
for (File srcFile : srcFiles) {
if (null == srcFile) {
continue;
}
// 如果只是压缩一个文件,则需要截取该文件的父目录
srcRootDir = srcFile.getCanonicalPath();
if (srcFile.isFile() || withSrcDir) {
// 若是文件,则将父目录完整路径都截取掉;若设置包含目录,则将上级目录全部截取掉,保留本目录名
srcRootDir = srcFile.getCanonicalFile().getParentFile().getCanonicalPath();
}
// 调用递归压缩方法进行目录或文件压缩
zip(srcFile, srcRootDir, out, filter);
out.flush();
}
} catch (IOException e) {
throw new UtilException(e);
}
return zipFile;
}
/**
* 对流中的数据加入到压缩文件,使用默认UTF-8编码
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param path 流数据在压缩文件中的路径或文件名
* @param data 要压缩的数据
* @return 压缩文件
* @throws UtilException IO异常
* @since 3.0.6
*/
public static File zip(File zipFile, String path, String data) throws UtilException {
return zip(zipFile, path, data, DEFAULT_CHARSET);
}
/**
* 对流中的数据加入到压缩文件
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param path 流数据在压缩文件中的路径或文件名
* @param data 要压缩的数据
* @param charset 编码
* @return 压缩文件
* @throws UtilException IO异常
* @since 3.2.2
*/
public static File zip(File zipFile, String path, String data, Charset charset)
throws UtilException {
return zip(zipFile, path, IoUtil.toStream(data, charset), charset);
}
/**
* 对流中的数据加入到压缩文件
* 使用默认编码UTF-8
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param path 流数据在压缩文件中的路径或文件名
* @param in 要压缩的源
* @return 压缩文件
* @throws UtilException IO异常
* @since 3.0.6
*/
public static File zip(File zipFile, String path, InputStream in) throws UtilException {
return zip(zipFile, path, in, DEFAULT_CHARSET);
}
/**
* 对流中的数据加入到压缩文件
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param path 流数据在压缩文件中的路径或文件名
* @param in 要压缩的源,默认关闭
* @param charset 编码
* @return 压缩文件
* @throws UtilException IO异常
* @since 3.2.2
*/
public static File zip(File zipFile, String path, InputStream in, Charset charset)
throws UtilException {
return zip(zipFile, new String[] {path}, new InputStream[] {in}, charset);
}
/**
* 对流中的数据加入到压缩文件
* 路径列表和流列表长度必须一致
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param paths 流数据在压缩文件中的路径或文件名
* @param ins 要压缩的源
* @return 压缩文件
* @throws UtilException IO异常
* @since 3.0.9
*/
public static File zip(File zipFile, String[] paths, InputStream[] ins) throws UtilException {
return zip(zipFile, paths, ins, DEFAULT_CHARSET);
}
/**
* 对流中的数据加入到压缩文件
* 路径列表和流列表长度必须一致
*
* @param zipFile 生成的Zip文件,包括文件名。注意:zipPath不能是srcPath路径下的子文件夹
* @param paths 流数据在压缩文件中的路径或文件名
* @param ins 要压缩的源,添加完成后自动关闭流
* @param charset 编码
* @return 压缩文件
* @throws UtilException IO异常
* @since 3.0.9
*/
public static File zip(File zipFile, String[] paths, InputStream[] ins, Charset charset)
throws UtilException {
if (ArrayUtil.isEmpty(paths) || ArrayUtil.isEmpty(ins)) {
throw new IllegalArgumentException("Paths or ins is empty !");
}
if (paths.length != ins.length) {
throw new IllegalArgumentException("Paths length is not equals to ins length !");
}
ZipOutputStream out = null;
try {
out = getZipOutputStream(zipFile, charset);
for (int i = 0; i < paths.length; i++) {
addFile(ins[i], paths[i], out);
}
} finally {
IoUtil.close(out);
}
return zipFile;
}
// ----------------------------------------------------------------------------------------------
// Unzip
/**
* 解压到文件名相同的目录中,默认编码UTF-8
*
* @param zipFilePath 压缩文件路径
* @return 解压的目录
* @throws UtilException IO异常
*/
public static File unzip(String zipFilePath) throws UtilException {
return unzip(zipFilePath, DEFAULT_CHARSET);
}
/**
* 解压到文件名相同的目录中
*
* @param zipFilePath 压缩文件路径
* @param charset 编码
* @return 解压的目录
* @throws UtilException IO异常
* @since 3.2.2
*/
public static File unzip(String zipFilePath, Charset charset) throws UtilException {
return unzip(FileUtil.file(zipFilePath), charset);
}
/**
* 解压到文件名相同的目录中,使用UTF-8编码
*
* @param zipFile 压缩文件
* @return 解压的目录
* @throws UtilException IO异常
* @since 3.2.2
*/
public static File unzip(File zipFile) throws UtilException {
return unzip(zipFile, DEFAULT_CHARSET);
}
/**
* 解压到文件名相同的目录中
*
* @param zipFile 压缩文件
* @param charset 编码
* @return 解压的目录
* @throws UtilException IO异常
* @since 3.2.2
*/
public static File unzip(File zipFile, Charset charset) throws UtilException {
return unzip(
zipFile, FileUtil.file(zipFile.getParentFile(), FileUtil.mainName(zipFile)), charset);
}
/**
* 解压,默认UTF-8编码
*
* @param zipFilePath 压缩文件的路径
* @param outFileDir 解压到的目录
* @return 解压的目录
* @throws UtilException IO异常
*/
public static File unzip(String zipFilePath, String outFileDir) throws UtilException {
return unzip(zipFilePath, outFileDir, DEFAULT_CHARSET);
}
/**
* 解压
*
* @param zipFilePath 压缩文件的路径
* @param outFileDir 解压到的目录
* @param charset 编码
* @return 解压的目录
* @throws UtilException IO异常
*/
public static File unzip(String zipFilePath, String outFileDir, Charset charset)
throws UtilException {
return unzip(FileUtil.file(zipFilePath), FileUtil.mkdir(outFileDir), charset);
}
/**
* 解压,默认使用UTF-8编码
*
* @param zipFile zip文件
* @param outFile 解压到的目录
* @return 解压的目录
* @throws UtilException IO异常
*/
public static File unzip(File zipFile, File outFile) throws UtilException {
return unzip(zipFile, outFile, DEFAULT_CHARSET);
}
/**
* 解压
*
* @param zipFile zip文件
* @param outFile 解压到的目录
* @param charset 编码
* @return 解压的目录
* @throws UtilException IO异常
* @since 3.2.2
*/
public static File unzip(File zipFile, File outFile, Charset charset) throws UtilException {
ZipFile zip;
try {
zip = new ZipFile(zipFile, charset);
} catch (IOException e) {
throw new IORuntimeException(e);
}
return unzip(zip, outFile);
}
/**
* 解压
*
* @param zipFile zip文件,附带编码信息,使用完毕自动关闭
* @param outFile 解压到的目录
* @return 解压的目录
* @throws IORuntimeException IO异常
* @since 4.5.8
*/
@SuppressWarnings("unchecked")
public static File unzip(ZipFile zipFile, File outFile) throws IORuntimeException {
try {
final Enumeration em = (Enumeration) zipFile.entries();
ZipEntry zipEntry;
File outItemFile;
while (em.hasMoreElements()) {
zipEntry = em.nextElement();
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
outItemFile = buildFile(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 创建对应目录
outItemFile.mkdirs();
} else {
// 写出文件
write(zipFile, zipEntry, outItemFile);
}
}
} finally {
IoUtil.close(zipFile);
}
return outFile;
}
/**
* 解压
* ZIP条目不使用高速缓冲。
*
* @param in zip文件流,使用完毕自动关闭
* @param outFile 解压到的目录
* @param charset 编码
* @return 解压的目录
* @throws UtilException IO异常
* @since 4.5.8
*/
public static File unzip(InputStream in, File outFile, Charset charset) throws UtilException {
if (null == charset) {
charset = DEFAULT_CHARSET;
}
return unzip(new ZipInputStream(in, charset), outFile);
}
/**
* 解压
* ZIP条目不使用高速缓冲。
*
* @param zipStream zip文件流,包含编码信息
* @param outFile 解压到的目录
* @return 解压的目录
* @throws UtilException IO异常
* @since 4.5.8
*/
public static File unzip(ZipInputStream zipStream, File outFile) throws UtilException {
try {
ZipEntry zipEntry;
File outItemFile;
while (null != (zipEntry = zipStream.getNextEntry())) {
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 目录
outItemFile.mkdirs();
} else {
// 文件
FileUtil.writeFromStream(zipStream, outItemFile);
}
}
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(zipStream);
}
return outFile;
}
/**
* 从Zip文件中提取指定的文件为bytes
*
* @param zipFilePath Zip文件
* @param name 文件名,如果存在于子文件夹中,此文件名必须包含目录名,例如images/aaa.txt
* @return 文件内容bytes
* @since 4.1.8
*/
public static byte[] unzipFileBytes(String zipFilePath, String name) {
return unzipFileBytes(zipFilePath, DEFAULT_CHARSET, name);
}
/**
* 从Zip文件中提取指定的文件为bytes
*
* @param zipFilePath Zip文件
* @param charset 编码
* @param name 文件名,如果存在于子文件夹中,此文件名必须包含目录名,例如images/aaa.txt
* @return 文件内容bytes
* @since 4.1.8
*/
public static byte[] unzipFileBytes(String zipFilePath, Charset charset, String name) {
return unzipFileBytes(FileUtil.file(zipFilePath), charset, name);
}
/**
* 从Zip文件中提取指定的文件为bytes
*
* @param zipFile Zip文件
* @param name 文件名,如果存在于子文件夹中,此文件名必须包含目录名,例如images/aaa.txt
* @return 文件内容bytes
* @since 4.1.8
*/
public static byte[] unzipFileBytes(File zipFile, String name) {
return unzipFileBytes(zipFile, DEFAULT_CHARSET, name);
}
/**
* 从Zip文件中提取指定的文件为bytes
*
* @param zipFile Zip文件
* @param charset 编码
* @param name 文件名,如果存在于子文件夹中,此文件名必须包含目录名,例如images/aaa.txt
* @return 文件内容bytes
* @since 4.1.8
*/
@SuppressWarnings("unchecked")
public static byte[] unzipFileBytes(File zipFile, Charset charset, String name) {
ZipFile zipFileObj = null;
try {
zipFileObj = new ZipFile(zipFile, charset);
final Enumeration em = (Enumeration) zipFileObj.entries();
ZipEntry zipEntry;
while (em.hasMoreElements()) {
zipEntry = em.nextElement();
if ((false == zipEntry.isDirectory()) && name.equals(zipEntry.getName())) {
return IoUtil.readBytes(zipFileObj.getInputStream(zipEntry));
}
}
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(zipFileObj);
}
return null;
}
// ----------------------------------------------------------------------------- Gzip
/**
* Gzip压缩处理
*
* @param content 被压缩的字符串
* @param charset 编码
* @return 压缩后的字节流
* @throws UtilException IO异常
*/
public static byte[] gzip(String content, String charset) throws UtilException {
return gzip(StrUtil.bytes(content, charset));
}
/**
* Gzip压缩处理
*
* @param buf 被压缩的字节流
* @return 压缩后的字节流
* @throws UtilException IO异常
*/
public static byte[] gzip(byte[] buf) throws UtilException {
return gzip(new ByteArrayInputStream(buf), buf.length);
}
/**
* Gzip压缩文件
*
* @param file 被压缩的文件
* @return 压缩后的字节流
* @throws UtilException IO异常
*/
public static byte[] gzip(File file) throws UtilException {
BufferedInputStream in = null;
try {
in = FileUtil.getInputStream(file);
return gzip(in, (int) file.length());
} finally {
IoUtil.close(in);
}
}
/**
* Gzip压缩文件
*
* @param in 被压缩的流
* @return 压缩后的字节流
* @throws UtilException IO异常
* @since 4.1.18
*/
public static byte[] gzip(InputStream in) throws UtilException {
return gzip(in, DEFAULT_BYTE_ARRAY_LENGTH);
}
/**
* Gzip压缩文件
*
* @param in 被压缩的流
* @param length 预估长度
* @return 压缩后的字节流
* @throws UtilException IO异常
* @since 4.1.18
*/
public static byte[] gzip(InputStream in, int length) throws UtilException {
final ByteArrayOutputStream bos = new ByteArrayOutputStream(length);
GZIPOutputStream gos = null;
try {
gos = new GZIPOutputStream(bos);
IoUtil.copy(in, gos);
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(gos);
}
// 返回必须在关闭gos后进行,因为关闭时会自动执行finish()方法,保证数据全部写出
return bos.toByteArray();
}
/**
* Gzip解压缩处理
*
* @param buf 压缩过的字节流
* @param charset 编码
* @return 解压后的字符串
* @throws UtilException IO异常
*/
public static String unGzip(byte[] buf, String charset) throws UtilException {
return StrUtil.str(unGzip(buf), charset);
}
/**
* Gzip解压处理
*
* @param buf buf
* @return bytes
* @throws UtilException IO异常
*/
public static byte[] unGzip(byte[] buf) throws UtilException {
return unGzip(new ByteArrayInputStream(buf), buf.length);
}
/**
* Gzip解压处理
*
* @param in Gzip数据
* @return 解压后的数据
* @throws UtilException IO异常
*/
public static byte[] unGzip(InputStream in) throws UtilException {
return unGzip(in, DEFAULT_BYTE_ARRAY_LENGTH);
}
/**
* Gzip解压处理
*
* @param in Gzip数据
* @param length 估算长度,如果无法确定请传入{@link #DEFAULT_BYTE_ARRAY_LENGTH}
* @return 解压后的数据
* @throws UtilException IO异常
* @since 4.1.18
*/
public static byte[] unGzip(InputStream in, int length) throws UtilException {
GZIPInputStream gzi = null;
FastByteArrayOutputStream bos;
try {
gzi = (in instanceof GZIPInputStream) ? (GZIPInputStream) in : new GZIPInputStream(in);
bos = new FastByteArrayOutputStream(length);
IoUtil.copy(gzi, bos);
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(gzi);
}
// 返回必须在关闭gos后进行,因为关闭时会自动执行finish()方法,保证数据全部写出
return bos.toByteArray();
}
// ----------------------------------------------------------------------------- Zlib
/**
* Zlib压缩处理
*
* @param content 被压缩的字符串
* @param charset 编码
* @param level 压缩级别,1~9
* @return 压缩后的字节流
* @since 4.1.4
*/
public static byte[] zlib(String content, String charset, int level) {
return zlib(StrUtil.bytes(content, charset), level);
}
/**
* Zlib压缩文件
*
* @param file 被压缩的文件
* @param level 压缩级别
* @return 压缩后的字节流
* @since 4.1.4
*/
public static byte[] zlib(File file, int level) {
BufferedInputStream in = null;
try {
in = FileUtil.getInputStream(file);
return zlib(in, level, (int) file.length());
} finally {
IoUtil.close(in);
}
}
/**
* 打成Zlib压缩包
*
* @param buf 数据
* @param level 压缩级别,0~9
* @return 压缩后的bytes
* @since 4.1.4
*/
public static byte[] zlib(byte[] buf, int level) {
return zlib(new ByteArrayInputStream(buf), level, buf.length);
}
/**
* 打成Zlib压缩包
*
* @param in 数据流
* @param level 压缩级别,0~9
* @return 压缩后的bytes
* @since 4.1.19
*/
public static byte[] zlib(InputStream in, int level) {
return zlib(in, level, DEFAULT_BYTE_ARRAY_LENGTH);
}
/**
* 打成Zlib压缩包
*
* @param in 数据流
* @param level 压缩级别,0~9
* @param length 预估大小
* @return 压缩后的bytes
* @since 4.1.19
*/
public static byte[] zlib(InputStream in, int level, int length) {
final ByteArrayOutputStream out = new ByteArrayOutputStream(length);
deflater(in, out, level, false);
return out.toByteArray();
}
/**
* Zlib解压缩处理
*
* @param buf 压缩过的字节流
* @param charset 编码
* @return 解压后的字符串
* @since 4.1.4
*/
public static String unZlib(byte[] buf, String charset) {
return StrUtil.str(unZlib(buf), charset);
}
/**
* 解压缩zlib
*
* @param buf 数据
* @return 解压后的bytes
* @since 4.1.4
*/
public static byte[] unZlib(byte[] buf) {
return unZlib(new ByteArrayInputStream(buf), buf.length);
}
/**
* 解压缩zlib
*
* @param in 数据流
* @return 解压后的bytes
* @since 4.1.19
*/
public static byte[] unZlib(InputStream in) {
return unZlib(in, DEFAULT_BYTE_ARRAY_LENGTH);
}
/**
* 解压缩zlib
*
* @param in 数据流
* @param length 预估长度
* @return 解压后的bytes
* @since 4.1.19
*/
public static byte[] unZlib(InputStream in, int length) {
final ByteArrayOutputStream out = new ByteArrayOutputStream(length);
inflater(in, out, false);
return out.toByteArray();
}
/**
* 获取Zip文件中指定目录下的所有文件,只显示文件,不显示目录
*
* @param zipFile Zip文件
* @param dir 目录前缀
* @return 文件列表
* @since 4.6.6
*/
public static List listFileNames(ZipFile zipFile, String dir) {
if (StrUtil.isNotBlank(dir)) {
// 目录尾部添加"/"
dir = StrUtil.addSuffixIfNot(dir, StrUtil.SLASH);
}
final List fileNames = new ArrayList<>();
String name;
for (ZipEntry entry : Collections.list(zipFile.entries())) {
name = entry.getName();
if (StrUtil.isEmpty(dir) || name.startsWith(dir)) {
final String nameSuffix = StrUtil.removePrefix(name, dir);
if (StrUtil.isNotEmpty(nameSuffix)
&& false == StrUtil.contains(nameSuffix, CharUtil.SLASH)) {
fileNames.add(nameSuffix);
}
}
}
return fileNames;
}
// ----------------------------------------------------------------------------------------------
// Private method start
/**
* 获得 {@link ZipOutputStream}
*
* @param zipFile 压缩文件
* @param charset 编码
* @return {@link ZipOutputStream}
*/
private static ZipOutputStream getZipOutputStream(File zipFile, Charset charset) {
return getZipOutputStream(FileUtil.getOutputStream(zipFile), charset);
}
/**
* 获得 {@link ZipOutputStream}
*
* @param out 压缩文件流
* @param charset 编码
* @return {@link ZipOutputStream}
*/
private static ZipOutputStream getZipOutputStream(OutputStream out, Charset charset) {
return new ZipOutputStream(out, ObjectUtil.defaultIfNull(charset, DEFAULT_CHARSET));
}
/**
* 递归压缩文件夹
* srcRootDir决定了路径截取的位置,例如:
* file的路径为d:/a/b/c/d.txt,srcRootDir为d:/a/b,则压缩后的文件与目录为结构为c/d.txt
*
* @param out 压缩文件存储对象
* @param srcRootDir 被压缩的文件夹根目录
* @param file 当前递归压缩的文件或目录对象
* @param filter 文件过滤器,通过实现此接口,自定义要过滤的文件(过滤掉哪些文件或文件夹不加入压缩)
* @throws UtilException IO异常
*/
private static void zip(File file, String srcRootDir, ZipOutputStream out, FileFilter filter)
throws UtilException {
if (null == file || (null != filter && false == filter.accept(file))) {
return;
}
final String subPath = FileUtil.subPath(srcRootDir, file); // 获取文件相对于压缩文件夹根目录的子路径
if (file.isDirectory()) { // 如果是目录,则压缩压缩目录中的文件或子目录
final File[] files = file.listFiles();
if (ArrayUtil.isEmpty(files) && StrUtil.isNotEmpty(subPath)) {
// 加入目录,只有空目录时才加入目录,非空时会在创建文件时自动添加父级目录
addDir(subPath, out);
}
// 压缩目录下的子文件或目录
for (File childFile : files) {
zip(childFile, srcRootDir, out, filter);
}
} else { // 如果是文件或其它符号,则直接压缩该文件
addFile(file, subPath, out);
}
}
/**
* 添加文件到压缩包
*
* @param file 需要压缩的文件
* @param path 在压缩文件中的路径
* @param out 压缩文件存储对象
* @throws UtilException IO异常
* @since 4.0.5
*/
private static void addFile(File file, String path, ZipOutputStream out) throws UtilException {
addFile(FileUtil.getInputStream(file), path, out);
}
/**
* 添加文件流到压缩包,添加后关闭流
*
* @param in 需要压缩的输入流
* @param path 压缩的路径
* @param out 压缩文件存储对象
* @throws UtilException IO异常
*/
private static void addFile(InputStream in, String path, ZipOutputStream out)
throws UtilException {
if (null == in) {
return;
}
try {
out.putNextEntry(new ZipEntry(path));
IoUtil.copy(in, out);
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(in);
closeEntry(out);
}
}
/**
* 在压缩包中新建目录
*
* @param path 压缩的路径
* @param out 压缩文件存储对象
* @throws UtilException IO异常
*/
private static void addDir(String path, ZipOutputStream out) throws UtilException {
path = StrUtil.addSuffixIfNot(path, StrUtil.SLASH);
try {
out.putNextEntry(new ZipEntry(path));
} catch (IOException e) {
throw new UtilException(e);
} finally {
closeEntry(out);
}
}
/**
* 判断压缩文件保存的路径是否为源文件路径的子文件夹,如果是,则抛出异常(防止无限递归压缩的发生)
*
* @param zipFile 压缩后的产生的文件路径
* @param srcFiles 被压缩的文件或目录
*/
private static void validateFiles(File zipFile, File... srcFiles) throws UtilException {
if (zipFile.isDirectory()) {
throw new UtilException("Zip file [{}] must not be a directory !", zipFile.getAbsoluteFile());
}
for (File srcFile : srcFiles) {
if (null == srcFile) {
continue;
}
if (false == srcFile.exists()) {
throw new UtilException(StrUtil.format("File [{}] not exist!", srcFile.getAbsolutePath()));
}
try {
final File parentFile = zipFile.getCanonicalFile().getParentFile();
// 压缩文件不能位于被压缩的目录内
if (srcFile.isDirectory()
&& parentFile.getCanonicalPath().contains(srcFile.getCanonicalPath())) {
throw new UtilException(
"Zip file path [{}] must not be the child directory of [{}] !",
zipFile.getCanonicalPath(),
srcFile.getCanonicalPath());
}
} catch (IOException e) {
throw new UtilException(e);
}
}
}
/**
* 关闭当前Entry,继续下一个Entry
*
* @param out ZipOutputStream
*/
private static void closeEntry(ZipOutputStream out) {
try {
out.closeEntry();
} catch (IOException e) {
// ignore
}
}
/**
* 从Zip中读取文件流并写出到文件
*
* @param zipFile Zip文件
* @param zipEntry zip文件中的子文件
* @param outItemFile 输出到的文件
* @throws IORuntimeException IO异常
*/
private static void write(ZipFile zipFile, ZipEntry zipEntry, File outItemFile)
throws IORuntimeException {
InputStream in = null;
try {
in = zipFile.getInputStream(zipEntry);
FileUtil.writeFromStream(in, outItemFile);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
IoUtil.close(in);
}
}
/**
* 将Zlib流解压到out中
*
* @param in zlib数据流
* @param out 输出
* @param nowrap true表示兼容Gzip压缩
*/
private static void inflater(InputStream in, OutputStream out, boolean nowrap) {
final InflaterOutputStream ios =
(out instanceof InflaterOutputStream)
? (InflaterOutputStream) out
: new InflaterOutputStream(out, new Inflater(nowrap));
IoUtil.copy(in, ios);
try {
ios.finish();
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 将普通数据流压缩成zlib到out中
*
* @param in zlib数据流
* @param out 输出
* @param level 压缩级别,0~9
* @param nowrap true表示兼容Gzip压缩
*/
private static void deflater(InputStream in, OutputStream out, int level, boolean nowrap) {
final DeflaterOutputStream ios =
(out instanceof DeflaterOutputStream)
? (DeflaterOutputStream) out
: new DeflaterOutputStream(out, new Deflater(level, nowrap));
IoUtil.copy(in, ios);
try {
ios.finish();
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 根据压缩包中的路径构建目录结构,在Win下直接构建,在Linux下拆分路径单独构建
*
* @param outFile 最外部路径
* @param fileName 文件名,可以包含路径
* @return 文件或目录
* @since 5.0.5
*/
private static File buildFile(File outFile, String fileName) {
if (false == FileUtil.isWindows()
// 检查文件名中是否包含"/",不考虑以"/"结尾的情况
&& fileName.lastIndexOf(CharUtil.SLASH, fileName.length() - 2) > 0) {
// 在Linux下多层目录创建存在问题,/会被当成文件名的一部分,此处做处理
// 使用/拆分路径(zip中无\),级联创建父目录
final List pathParts = StrUtil.split(fileName, '/', false, true);
final int lastPartIndex = pathParts.size() - 1; // 目录个数
for (int i = 0; i < lastPartIndex; i++) {
// 由于路径拆分,slip不检查,在最后一步检查
outFile = new File(outFile, pathParts.get(i));
}
//noinspection ResultOfMethodCallIgnored
outFile.mkdirs();
// 最后一个部分如果非空,作为文件名
fileName = pathParts.get(lastPartIndex);
}
return FileUtil.file(outFile, fileName);
}
// ----------------------------------------------------------------------------------------------
// Private method end
}