代码——将zip压缩文件按指定目录层级拆分成子zip文件

将zip压缩文件按指定目录层级拆分成子zip文件的工具方法,主要逻辑是读取zip压缩文件、分析目录结构并进行分组、按分组重新生成zip压缩文件

示例如下:

如A.zip文件,内部结构为:
– 文件夹A1
– 文件夹A1 – 文件夹B1
– 文件夹A1 – 文件夹B1 – 文件1.xlsx
– 文件夹A1 – 文件夹B1 – 文件2.docx
– 文件夹A1 – 文件夹B1 – 文件夹C1
– 文件夹A1 – 文件夹B1 – 文件夹C1 – 文件3.mp4
– 文件夹A1 – 文件夹B1 – 文件夹C1 – 文件4.wmv
– 文件夹A2
– 文件夹A2 – 文件夹B2
– 文件夹A2 – 文件夹B2 – 文件5.txt
– 文件夹A2 – 文件夹B2 – 文件夹C2
– 文件夹A2 – 文件夹B2 – 文件夹C2 – 文件6.jpeg
调用方法splitZipFileAndCreateChildZipFileGroupByFolder(zipFileInputStream, 2)后,会在指定目录下生成两个zip文件,分别是
文件夹B1.zip,内部结构为:
– 文件1.xlsx
– 文件2.docx
– 文件夹C1
– 文件夹C1 – 文件3.mp4
– 文件夹C1 – 文件4.wmv
文件夹B2.zip,内部结构为:
– 文件5.txt
– 文件夹C2
– 文件夹C2 – 文件6.jpeg

代码如下:

  1. import com.nd.edu.base.common.ExceptionDefinition;
  2. import com.nd.gaea.core.utils.DateUtils;
  3. import java.io.ByteArrayInputStream;
  4. import java.io.ByteArrayOutputStream;
  5. import java.io.File;
  6. import java.io.FileOutputStream;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.io.InputStreamReader;
  10. import java.nio.charset.Charset;
  11. import java.util.HashMap;
  12. import java.util.Map;
  13. import java.util.zip.ZipEntry;
  14. import java.util.zip.ZipInputStream;
  15. import java.util.zip.ZipOutputStream;
  16. import lombok.extern.slf4j.Slf4j;
  17. import org.apache.commons.io.IOUtils;
  18. @Slf4j
  19. public class ZipFileUtil {
  20. private static final String FOLDER_SEPARATOR = "/";
  21. /**
  22. * 拆分zip内按文件夹分组并重新生成子zip文件
  23. * @param zipFileInputStream zip文件输入流
  24. * @param level 要拆分的层级维度 1-表示根目录下的一级目录
  25. */
  26. public static void splitZipFileAndCreateChildZipFileGroupByFolder(
  27. InputStream zipFileInputStream, int level) {
  28. try {
  29. byte[] bytes = IOUtils.toByteArray(zipFileInputStream);
  30. IOUtils.closeQuietly(zipFileInputStream);
  31. Map<String, Map<String, byte[]>> folderFileNameContentMap =
  32. FileUtil.readZipFileByteArrayGroupByFolder(bytes, level);
  33. if (folderFileNameContentMap.isEmpty()) {
  34. return;
  35. }
  36. for (String folderName : folderFileNameContentMap.keySet()) {
  37. byte[] childZipByteArr =
  38. createZipFileByteArray(folderName, folderFileNameContentMap.get(folderName));
  39. createAndWriteFile("temp", folderName, "zip", childZipByteArr);
  40. }
  41. } catch (Exception ex) {
  42. log.error("splitZipAndCreateChildZipGroupByFolder:{}", ex.getMessage());
  43. }
  44. }
  45. /**
  46. * 从zip文件中按指定层级的目录对文件进行分组,获取文件字节数组
  47. *
  48. * @param contents zip文件字节数组
  49. * @param level 要按几级目录分组 1-表示根目录下的一级目录
  50. * @return 分组目录name->文件name和字节数组的map
  51. */
  52. public static Map<String, Map<String, byte[]>> readZipFileByteArrayGroupByFolder(
  53. byte[] contents, int level) {
  54. Map<String, Map<String, byte[]>> folderZipFileMap = new HashMap<>(100);
  55. try {
  56. ZipInputStream zipInput =
  57. new ZipInputStream(new ByteArrayInputStream(contents), Charset.forName("GBK"));
  58. ZipEntry zip = null;
  59. while ((zip = zipInput.getNextEntry()) != null) {
  60. // zip.getName()如果是文件夹,返回的是XXX/,末尾有个/
  61. String[] pathArr = zip.getName().split(FOLDER_SEPARATOR);
  62. if (pathArr.length < level) {
  63. // 忽略指定层级之上的目录和文件
  64. continue;
  65. }
  66. String baseFolder = pathArr[level - 1];
  67. if (zip.isDirectory()) {
  68. if (pathArr.length == level) {
  69. // 说明层级是要进行分组的文件夹
  70. folderZipFileMap.put(baseFolder, new HashMap<>(10));
  71. } else {
  72. // 说明是子文件夹,需要将路径处理去掉指定层级前的部分
  73. String[] newPathArr = new String[pathArr.length - level];
  74. System.arraycopy(pathArr, level, newPathArr, 0, pathArr.length - level);
  75. String newFolderPath = StringUtils.join(newPathArr);
  76. folderZipFileMap.get(baseFolder).put(newFolderPath, readContent(zipInput));
  77. }
  78. } else {
  79. if (pathArr.length > level) {
  80. // 仅处理分组层级下的文件和子目录里的文件
  81. String[] newPathArr = new String[pathArr.length - level];
  82. System.arraycopy(pathArr, level, newPathArr, 0, pathArr.length - level);
  83. String fileName = StringUtils.join(newPathArr, FOLDER_SEPARATOR);
  84. folderZipFileMap.get(baseFolder).put(fileName, readContent(zipInput));
  85. }
  86. }
  87. }
  88. zipInput.closeEntry();
  89. return folderZipFileMap;
  90. } catch (IOException e) {
  91. throw ExceptionDefinition.ZIP_READ_FAIL.create();
  92. }
  93. }
  94. /**
  95. * 生成zip压缩文件字节数组
  96. *
  97. * @param zipFileName zip文件名
  98. * @param innerFileMap 压缩包内要写入的文件 文件name->文件字节数组
  99. */
  100. public static byte[] createZipFileByteArray(
  101. String zipFileName, Map<String, byte[]> innerFileMap) {
  102. byte[] fileByteArr = null;
  103. try {
  104. try (ByteArrayOutputStream zipArrOutputStream = new ByteArrayOutputStream()) {
  105. try (ZipOutputStream zipOutputStream = new ZipOutputStream(zipArrOutputStream)) {
  106. // 先生成好目录结构
  107. for (String fileName : innerFileMap.keySet()) {
  108. ByteArrayInputStream fileInputStream =
  109. new ByteArrayInputStream(innerFileMap.get(fileName));
  110. if (fileName.contains(".")) {
  111. continue;
  112. }
  113. // 如果是文件夹,需要在末尾加/
  114. zipOutputStream.putNextEntry(new ZipEntry(fileName + "/"));
  115. zipOutputStream.write(IOUtils.toByteArray(fileInputStream));
  116. }
  117. for (String fileName : innerFileMap.keySet()) {
  118. ByteArrayInputStream fileInputStream =
  119. new ByteArrayInputStream(innerFileMap.get(fileName));
  120. if (!fileName.contains(".")) {
  121. continue;
  122. }
  123. zipOutputStream.putNextEntry(new ZipEntry(fileName));
  124. zipOutputStream.write(IOUtils.toByteArray(fileInputStream));
  125. }
  126. }
  127. fileByteArr = zipArrOutputStream.toByteArray();
  128. }
  129. } catch (Exception ex) {
  130. log.error("生成zip压缩文件{}失败:{}", zipFileName, ex.getMessage());
  131. }
  132. return fileByteArr;
  133. }
  134. /**
  135. * 读取zip文件并返回字节数组
  136. *
  137. * @param zipInput ZipInputStream
  138. * @return byte[]
  139. * @throws IOException
  140. */
  141. private static byte[] readContent(ZipInputStream zipInput) throws IOException {
  142. ByteArrayOutputStream out = new ByteArrayOutputStream();
  143. byte[] buffer = new byte[1024];
  144. int count = -1;
  145. while ((count = zipInput.read(buffer)) != -1) {
  146. out.write(buffer, 0, count);
  147. }
  148. out.close();
  149. return out.toByteArray();
  150. }
  151. }