All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.dahuatech.hutool.core.util.ZipUtil Maven / Gradle / Ivy

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 }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy