6518aa9a2a511324d8541b4ba95994bb11b10fd8.svn-base 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. *
  3. *
  4. * 商用软件 Copyright (c) 2010 北京金水技术发展有限公司.
  5. * All Rights Reserved.
  6. * 擅自复制或传播本程序的部分或全部属非法行为!
  7. *
  8. * 北京金水技术发展有限公司
  9. */
  10. package cn.com.goldenwater.dcproj.utils;
  11. import lombok.extern.slf4j.Slf4j;
  12. import org.apache.tools.zip.ZipEntry;
  13. import org.apache.tools.zip.ZipFile;
  14. import org.apache.tools.zip.ZipOutputStream;
  15. import java.io.*;
  16. import java.util.Enumeration;
  17. import java.util.zip.CRC32;
  18. import java.util.zip.CheckedOutputStream;
  19. import java.util.zip.Deflater;
  20. import java.util.zip.ZipException;
  21. /**
  22. * @author <a href="mailto:chinawar3@gmail.com">jiayp</a>
  23. * @author <a href="mailto:dashuizhuyu@qq.com">liyz</a>
  24. * @date 2010-8-20
  25. * @date 2019-7-10
  26. */
  27. @Slf4j
  28. public class ZipUtil {
  29. /**
  30. * 是否创建源目录
  31. */
  32. private static boolean isCreateSrcDir = false;
  33. private static int BUFFER_SIZE = 4098;
  34. private static String ENCODE = "GBK";
  35. /**
  36. * 测试方法
  37. */
  38. public static void zip(String src, String archive, String comment)
  39. throws FileNotFoundException, IOException {
  40. // ----压缩文件:
  41. FileOutputStream f = new FileOutputStream(archive);
  42. // 使用指定校验和创建输出流
  43. CheckedOutputStream csum = new CheckedOutputStream(f, new CRC32());
  44. ZipOutputStream zos = new ZipOutputStream(csum);
  45. // 支持中文
  46. zos.setEncoding(ENCODE);
  47. BufferedOutputStream out = new BufferedOutputStream(zos);
  48. // 设置压缩包注释
  49. zos.setComment(comment);
  50. // 启用压缩
  51. zos.setMethod(ZipOutputStream.DEFLATED);
  52. // 压缩级别为最强压缩,但时间要花得多一点
  53. zos.setLevel(Deflater.BEST_COMPRESSION);
  54. File srcFile = new File(src);
  55. if (!srcFile.exists()
  56. || (srcFile.isDirectory() && srcFile.list().length == 0)) {
  57. throw new FileNotFoundException(
  58. "File must exist and ZIP file must have at least one entry.");
  59. }
  60. // 获取压缩源所在父目录
  61. src = src.replaceAll("\\\\", "/");
  62. String prefixDir = null;
  63. if (srcFile.isFile()) {
  64. prefixDir = src.substring(0, src.lastIndexOf("/") + 1);
  65. } else {
  66. prefixDir = (src.replaceAll("/$", "") + "/");
  67. }
  68. // 如果不是根目录
  69. if (prefixDir.indexOf("/") != (prefixDir.length() - 1)
  70. && isCreateSrcDir) {
  71. prefixDir = prefixDir.replaceAll("[^/]+/$", "");
  72. }
  73. // 开始压缩
  74. writeRecursive(zos, out, srcFile, prefixDir);
  75. out.close();
  76. // 注:校验和要在流关闭后才准备,一定要放在流被关闭后使用
  77. log.debug("Checksum: " + csum.getChecksum().getValue());
  78. }
  79. public static void readByApacheZipFile(File file, String decompressDir)
  80. throws IOException, FileNotFoundException, ZipException {
  81. // 支持中文
  82. ZipFile zf = new ZipFile(file, ENCODE);
  83. readByApacheZipFile(decompressDir, zf);
  84. }
  85. /**
  86. * 使用 org.apache.tools.zip.ZipFile 解压文件,它与 java 类库中的 java.util.zip.ZipFile
  87. * 使用方式是一致的,只不过多了设置编码方式的 接口。
  88. * <p>
  89. * 注,apache 没有提供 ZipInputStream 类,所以只能使用它提供的ZipFile 来读取压缩文件。
  90. *
  91. * @param archive 压缩包路径
  92. * @param decompressDir 解压路径
  93. * @throws IOException io异常
  94. * @throws FileNotFoundException 文件未发现异常
  95. * @throws ZipException zip异常
  96. */
  97. public static void unzip(String archive, String decompressDir)
  98. throws IOException, FileNotFoundException, ZipException {
  99. // 支持中文
  100. ZipFile zf = new ZipFile(archive, ENCODE);
  101. readByApacheZipFile(decompressDir, zf);
  102. }
  103. /**
  104. * 使用 org.apache.tools.zip.ZipFile 解压文件,它与 java 类库中的 java.util.zip.ZipFile
  105. * 使用方式是一致的,只不过多了设置编码方式的 接口。
  106. * <p>
  107. * 注,apache 没有提供 ZipInputStream 类,所以只能使用它提供的ZipFile 来读取压缩文件。
  108. *
  109. * @param file 压缩包文件
  110. * @param decompressDir 解压路径
  111. * @throws IOException io异常
  112. * @throws FileNotFoundException 文件未发现异常
  113. * @throws ZipException zip异常
  114. */
  115. public static void unzip(File file, String decompressDir)
  116. throws IOException, FileNotFoundException, ZipException {
  117. // 支持中文
  118. ZipFile zf = new ZipFile(file, ENCODE);
  119. readByApacheZipFile(decompressDir, zf);
  120. }
  121. /**
  122. * @param decompressDir decompressDir
  123. * @param zf zf
  124. * @throws FileNotFoundException FileNotFoundException
  125. * @throws IOException IOException
  126. * @throws ZipException ZipException
  127. */
  128. private static void readByApacheZipFile(String decompressDir, ZipFile zf)
  129. throws FileNotFoundException, IOException, ZipException {
  130. BufferedInputStream bi = null;
  131. Enumeration<?> e = zf.getEntries();
  132. while (e.hasMoreElements()) {
  133. ZipEntry ze2 = (ZipEntry) e.nextElement();
  134. String entryName = ze2.getName();
  135. String path = decompressDir + "/" + entryName;
  136. if (ze2.isDirectory()) {
  137. log.debug("正在创建解压目录 - " + entryName);
  138. File decompressDirFile = new File(path);
  139. if (!decompressDirFile.exists()) {
  140. decompressDirFile.mkdirs();
  141. }
  142. } else {
  143. log.debug("正在创建解压文件 - " + entryName);
  144. String fileDir = path.substring(0, path.lastIndexOf("/"));
  145. File fileDirFile = new File(fileDir);
  146. if (!fileDirFile.exists()) {
  147. fileDirFile.mkdirs();
  148. }
  149. BufferedOutputStream bos = new BufferedOutputStream(
  150. new FileOutputStream(decompressDir + "/" + entryName));
  151. bi = new BufferedInputStream(zf.getInputStream(ze2));
  152. byte[] readContent = new byte[BUFFER_SIZE];
  153. int readCount = bi.read(readContent);
  154. while (readCount != -1) {
  155. // 解压对文件二进制加密
  156. bos.write(readContent, 0, readCount);
  157. readCount = bi.read(readContent);
  158. }
  159. bos.close();
  160. bi.close();
  161. }
  162. }
  163. zf.close();
  164. }
  165. /**
  166. * 递归压缩
  167. * <p>
  168. * 使用 org.apache.tools.zip.ZipOutputStream 类进行压缩,它的好处就是支持中文路径, 而Java类库中的
  169. * java.util.zip.ZipOutputStream 压缩中文文件名时压缩包会出现乱码。 使用 apache 中的这个类与 java
  170. * 类库中的用法是一新的,只是能设置编码方式了。
  171. *
  172. * @param zos zos
  173. * @param bo bo
  174. * @param srcFile srcFile
  175. * @param prefixDir prefixDir
  176. * @throws IOException IOException
  177. * @throws FileNotFoundException FileNotFoundException
  178. */
  179. private static void writeRecursive(ZipOutputStream zos,
  180. BufferedOutputStream bo, File srcFile, String prefixDir)
  181. throws IOException, FileNotFoundException {
  182. ZipEntry zipEntry;
  183. String filePath = srcFile.getAbsolutePath().replaceAll("\\\\", "/")
  184. .replaceAll("//", "/");
  185. if (srcFile.isDirectory()) {
  186. filePath = filePath.replaceAll("/$", "") + "/";
  187. }
  188. String entryName = filePath.replace(prefixDir, "").replaceAll("/$", "");
  189. if (srcFile.isDirectory()) {
  190. if (!"".equals(entryName)) {
  191. log.debug("正在创建目录 - " + srcFile.getAbsolutePath()
  192. + " entryName=" + entryName);
  193. // 如果是目录,则需要在写目录后面加上 /
  194. zipEntry = new ZipEntry(entryName + "/");
  195. zos.putNextEntry(zipEntry);
  196. }
  197. File[] srcFiles = srcFile.listFiles();
  198. for (int i = 0; i < srcFiles.length; i++) {
  199. writeRecursive(zos, bo, srcFiles[i], prefixDir);
  200. }
  201. } else {
  202. log.debug("正在写文件 - " + srcFile.getAbsolutePath() + " entryName="
  203. + entryName);
  204. BufferedInputStream bi = new BufferedInputStream(
  205. new FileInputStream(srcFile));
  206. // 开始写入新的ZIP文件条目并将流定位到条目数据的开始处
  207. zipEntry = new ZipEntry(entryName);
  208. zos.putNextEntry(zipEntry);
  209. byte[] buffer = new byte[BUFFER_SIZE];
  210. int readCount = bi.read(buffer);
  211. while (readCount != -1) {
  212. bo.write(buffer, 0, readCount);
  213. readCount = bi.read(buffer);
  214. }
  215. // 注,在使用缓冲流写压缩文件时,一个条件完后一定要刷新一把,不
  216. // 然可能有的内容就会存入到后面条目中去了
  217. bo.flush();
  218. // 文件读完后关闭
  219. bi.close();
  220. }
  221. }
  222. }