/* * * * 商用软件 Copyright (c) 2010 北京金水技术发展有限公司. * All Rights Reserved. * 擅自复制或传播本程序的部分或全部属非法行为! * * 北京金水技术发展有限公司 */ package cn.com.goldenwater.dcproj.utils; import lombok.extern.slf4j.Slf4j; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; import org.apache.tools.zip.ZipOutputStream; import java.io.*; import java.util.Enumeration; import java.util.zip.CRC32; import java.util.zip.CheckedOutputStream; import java.util.zip.Deflater; import java.util.zip.ZipException; /** * @author jiayp * @author liyz * @date 2010-8-20 * @date 2019-7-10 */ @Slf4j public class ZipUtil { /** * 是否创建源目录 */ private static boolean isCreateSrcDir = false; private static int BUFFER_SIZE = 4098; private static String ENCODE = "GBK"; /** * 测试方法 */ public static void zip(String src, String archive, String comment) throws FileNotFoundException, IOException { // ----压缩文件: FileOutputStream f = new FileOutputStream(archive); // 使用指定校验和创建输出流 CheckedOutputStream csum = new CheckedOutputStream(f, new CRC32()); ZipOutputStream zos = new ZipOutputStream(csum); // 支持中文 zos.setEncoding(ENCODE); BufferedOutputStream out = new BufferedOutputStream(zos); // 设置压缩包注释 zos.setComment(comment); // 启用压缩 zos.setMethod(ZipOutputStream.DEFLATED); // 压缩级别为最强压缩,但时间要花得多一点 zos.setLevel(Deflater.BEST_COMPRESSION); File srcFile = new File(src); if (!srcFile.exists() || (srcFile.isDirectory() && srcFile.list().length == 0)) { throw new FileNotFoundException( "File must exist and ZIP file must have at least one entry."); } // 获取压缩源所在父目录 src = src.replaceAll("\\\\", "/"); String prefixDir = null; if (srcFile.isFile()) { prefixDir = src.substring(0, src.lastIndexOf("/") + 1); } else { prefixDir = (src.replaceAll("/$", "") + "/"); } // 如果不是根目录 if (prefixDir.indexOf("/") != (prefixDir.length() - 1) && isCreateSrcDir) { prefixDir = prefixDir.replaceAll("[^/]+/$", ""); } // 开始压缩 writeRecursive(zos, out, srcFile, prefixDir); out.close(); // 注:校验和要在流关闭后才准备,一定要放在流被关闭后使用 log.debug("Checksum: " + csum.getChecksum().getValue()); } public static void readByApacheZipFile(File file, String decompressDir) throws IOException, FileNotFoundException, ZipException { // 支持中文 ZipFile zf = new ZipFile(file, ENCODE); readByApacheZipFile(decompressDir, zf); } /** * 使用 org.apache.tools.zip.ZipFile 解压文件,它与 java 类库中的 java.util.zip.ZipFile * 使用方式是一致的,只不过多了设置编码方式的 接口。 *

* 注,apache 没有提供 ZipInputStream 类,所以只能使用它提供的ZipFile 来读取压缩文件。 * * @param archive 压缩包路径 * @param decompressDir 解压路径 * @throws IOException io异常 * @throws FileNotFoundException 文件未发现异常 * @throws ZipException zip异常 */ public static void unzip(String archive, String decompressDir) throws IOException, FileNotFoundException, ZipException { // 支持中文 ZipFile zf = new ZipFile(archive, ENCODE); readByApacheZipFile(decompressDir, zf); } /** * 使用 org.apache.tools.zip.ZipFile 解压文件,它与 java 类库中的 java.util.zip.ZipFile * 使用方式是一致的,只不过多了设置编码方式的 接口。 *

* 注,apache 没有提供 ZipInputStream 类,所以只能使用它提供的ZipFile 来读取压缩文件。 * * @param file 压缩包文件 * @param decompressDir 解压路径 * @throws IOException io异常 * @throws FileNotFoundException 文件未发现异常 * @throws ZipException zip异常 */ public static void unzip(File file, String decompressDir) throws IOException, FileNotFoundException, ZipException { // 支持中文 ZipFile zf = new ZipFile(file, ENCODE); readByApacheZipFile(decompressDir, zf); } /** * @param decompressDir decompressDir * @param zf zf * @throws FileNotFoundException FileNotFoundException * @throws IOException IOException * @throws ZipException ZipException */ private static void readByApacheZipFile(String decompressDir, ZipFile zf) throws FileNotFoundException, IOException, ZipException { BufferedInputStream bi = null; Enumeration e = zf.getEntries(); while (e.hasMoreElements()) { ZipEntry ze2 = (ZipEntry) e.nextElement(); String entryName = ze2.getName(); String path = decompressDir + "/" + entryName; if (ze2.isDirectory()) { log.debug("正在创建解压目录 - " + entryName); File decompressDirFile = new File(path); if (!decompressDirFile.exists()) { decompressDirFile.mkdirs(); } } else { log.debug("正在创建解压文件 - " + entryName); String fileDir = path.substring(0, path.lastIndexOf("/")); File fileDirFile = new File(fileDir); if (!fileDirFile.exists()) { fileDirFile.mkdirs(); } BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(decompressDir + "/" + entryName)); bi = new BufferedInputStream(zf.getInputStream(ze2)); byte[] readContent = new byte[BUFFER_SIZE]; int readCount = bi.read(readContent); while (readCount != -1) { // 解压对文件二进制加密 bos.write(readContent, 0, readCount); readCount = bi.read(readContent); } bos.close(); bi.close(); } } zf.close(); } /** * 递归压缩 *

* 使用 org.apache.tools.zip.ZipOutputStream 类进行压缩,它的好处就是支持中文路径, 而Java类库中的 * java.util.zip.ZipOutputStream 压缩中文文件名时压缩包会出现乱码。 使用 apache 中的这个类与 java * 类库中的用法是一新的,只是能设置编码方式了。 * * @param zos zos * @param bo bo * @param srcFile srcFile * @param prefixDir prefixDir * @throws IOException IOException * @throws FileNotFoundException FileNotFoundException */ private static void writeRecursive(ZipOutputStream zos, BufferedOutputStream bo, File srcFile, String prefixDir) throws IOException, FileNotFoundException { ZipEntry zipEntry; String filePath = srcFile.getAbsolutePath().replaceAll("\\\\", "/") .replaceAll("//", "/"); if (srcFile.isDirectory()) { filePath = filePath.replaceAll("/$", "") + "/"; } String entryName = filePath.replace(prefixDir, "").replaceAll("/$", ""); if (srcFile.isDirectory()) { if (!"".equals(entryName)) { log.debug("正在创建目录 - " + srcFile.getAbsolutePath() + " entryName=" + entryName); // 如果是目录,则需要在写目录后面加上 / zipEntry = new ZipEntry(entryName + "/"); zos.putNextEntry(zipEntry); } File[] srcFiles = srcFile.listFiles(); for (int i = 0; i < srcFiles.length; i++) { writeRecursive(zos, bo, srcFiles[i], prefixDir); } } else { log.debug("正在写文件 - " + srcFile.getAbsolutePath() + " entryName=" + entryName); BufferedInputStream bi = new BufferedInputStream( new FileInputStream(srcFile)); // 开始写入新的ZIP文件条目并将流定位到条目数据的开始处 zipEntry = new ZipEntry(entryName); zos.putNextEntry(zipEntry); byte[] buffer = new byte[BUFFER_SIZE]; int readCount = bi.read(buffer); while (readCount != -1) { bo.write(buffer, 0, readCount); readCount = bi.read(buffer); } // 注,在使用缓冲流写压缩文件时,一个条件完后一定要刷新一把,不 // 然可能有的内容就会存入到后面条目中去了 bo.flush(); // 文件读完后关闭 bi.close(); } } }