ZipReader.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. package com.yudianbank.utils;
  2. import com.fasterxml.jackson.core.JsonProcessingException;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import com.github.junrar.Archive;
  5. import com.github.junrar.exception.RarException;
  6. import com.github.junrar.rarfile.FileHeader;
  7. import com.google.common.collect.Lists;
  8. import com.google.common.collect.Maps;
  9. import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
  10. import org.apache.commons.compress.archivers.zip.ZipFile;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.beans.factory.annotation.Value;
  13. import org.springframework.stereotype.Component;
  14. import java.io.*;
  15. import java.util.*;
  16. import java.util.concurrent.ExecutorService;
  17. import java.util.concurrent.Executors;
  18. /**
  19. *
  20. * @author yudian-it
  21. * @date 2017/11/27
  22. */
  23. @Component
  24. public class ZipReader {
  25. @Autowired
  26. FileUtils fileUtils;
  27. @Value("${file.dir}")
  28. String fileDir;
  29. ExecutorService executors = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
  30. /**
  31. * 读取压缩文件
  32. * 文件压缩到统一目录fileDir下,并且命名使用压缩文件名+文件名因为文件名
  33. * 可能会重复(在系统中对于同一种类型的材料压缩文件内的文件是一样的,如果文件名
  34. * 重复,那么这里会被覆盖[同一个压缩文件中的不同目录中的相同文件名暂时不考虑])
  35. * <b>注:</b>
  36. * <p>
  37. * 文件名命名中的参数的说明:
  38. * 1.archiveName,为避免解压的文件中有重名的文件会彼此覆盖,所以加上了archiveName,因为在ufile中archiveName
  39. * 是不会重复的。
  40. * 2.level,这里层级结构的列表我是通过一个map来构造的,map的key是文件的名字,值是对应的文件,这样每次向map中
  41. * 加入节点的时候都会获取父节点是否存在,存在则会获取父节点的value并将当前节点加入到父节点的childList中(这里利用
  42. * 的是java语言的引用的特性)。
  43. * </p>
  44. * @param filePath
  45. */
  46. public String readZipFile(String filePath) {
  47. String archiveSeparator = "/";
  48. Map<String, FileNode> appender = Maps.newHashMap();
  49. String archiveFileName = fileUtils.getFileNameFromPath(filePath);
  50. try {
  51. ZipFile zipFile = new ZipFile(filePath, fileUtils.getFileEncodeUTFGBK(filePath));
  52. Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
  53. // 排序
  54. entries = sortZipEntries(entries);
  55. List<Map<String, ZipArchiveEntry>> entriesToBeExtracted = Lists.newArrayList();
  56. while (entries.hasMoreElements()){
  57. ZipArchiveEntry entry = entries.nextElement();
  58. String fullName = entry.getName();
  59. int level = fullName.split(archiveSeparator).length;
  60. // 展示名
  61. String originName = getLastFileName(fullName, archiveSeparator);
  62. String childName = level + "_" + originName;
  63. boolean directory = entry.isDirectory();
  64. if (!directory) {
  65. childName = archiveFileName + "_" + originName;
  66. entriesToBeExtracted.add(Collections.singletonMap(childName, entry));
  67. }
  68. String parentName = getLast2FileName(fullName, archiveSeparator, archiveFileName);
  69. parentName = (level-1) + "_" + parentName;
  70. FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory);
  71. addNodes(appender, parentName, node);
  72. appender.put(childName, node);
  73. }
  74. // 开启新的线程处理文件解压
  75. executors.submit(new ZipExtractorWorker(entriesToBeExtracted, zipFile, filePath));
  76. return new ObjectMapper().writeValueAsString(appender.get(""));
  77. } catch (IOException e) {
  78. e.printStackTrace();
  79. return null;
  80. }
  81. }
  82. /**
  83. * 排序zipEntries(对原来列表倒序)
  84. * @param entries
  85. */
  86. private Enumeration<ZipArchiveEntry> sortZipEntries(Enumeration<ZipArchiveEntry> entries) {
  87. List<ZipArchiveEntry> sortedEntries = Lists.newArrayList();
  88. while(entries.hasMoreElements()){
  89. sortedEntries.add(entries.nextElement());
  90. }
  91. Collections.sort(sortedEntries, Comparator.comparingInt(o -> o.getName().length()));
  92. return Collections.enumeration(sortedEntries);
  93. }
  94. public String unRar(String filePath){
  95. Map<String, FileNode> appender = Maps.newHashMap();
  96. try {
  97. Archive archive = new Archive(new File(filePath));
  98. List<FileHeader> headers = archive.getFileHeaders();
  99. headers = sortedHeaders(headers);
  100. String archiveFileName = fileUtils.getFileNameFromPath(filePath);
  101. List<Map<String, FileHeader>> headersToBeExtracted = Lists.newArrayList();
  102. for (FileHeader header : headers) {
  103. String fullName;
  104. if (header.isUnicode()) {
  105. fullName = header.getFileNameW();
  106. }else {
  107. fullName = header.getFileNameString();
  108. }
  109. // 展示名
  110. String originName = getLastFileName(fullName, "\\");
  111. String childName = originName;
  112. boolean directory = header.isDirectory();
  113. if (!directory) {
  114. childName = archiveFileName + "_" + originName;
  115. headersToBeExtracted.add(Collections.singletonMap(childName, header));
  116. }
  117. String parentName = getLast2FileName(fullName, "\\", archiveFileName);
  118. FileNode node = new FileNode(originName, childName, parentName, new ArrayList<>(), directory);
  119. addNodes(appender, parentName, node);
  120. appender.put(childName, node);
  121. }
  122. executors.submit(new RarExtractorWorker(headersToBeExtracted, archive, filePath));
  123. return new ObjectMapper().writeValueAsString(appender.get(""));
  124. } catch (RarException e) {
  125. e.printStackTrace();
  126. } catch (IOException e) {
  127. e.printStackTrace();
  128. }
  129. return null;
  130. }
  131. private void addNodes(Map<String, FileNode> appender, String parentName, FileNode node) {
  132. if (appender.containsKey(parentName)) {
  133. appender.get(parentName).getChildList().add(node);
  134. }else { // 根节点
  135. FileNode nodeRoot = new FileNode(parentName, parentName, "", new ArrayList<>(), true);
  136. nodeRoot.getChildList().add(node);
  137. appender.put("", nodeRoot);
  138. appender.put(parentName, nodeRoot);
  139. }
  140. }
  141. private List<FileHeader> sortedHeaders(List<FileHeader> headers) {
  142. List<FileHeader> sortedHeaders = new ArrayList<>();
  143. Map<Integer, FileHeader> mapHeaders = new TreeMap<>();
  144. headers.forEach(header -> mapHeaders.put(header.getFileNameW().length(), header));
  145. for (Map.Entry<Integer, FileHeader> entry : mapHeaders.entrySet()){
  146. for (FileHeader header : headers) {
  147. if (entry.getKey().intValue() == header.getFileNameW().length()) {
  148. sortedHeaders.add(header);
  149. }
  150. }
  151. }
  152. return sortedHeaders;
  153. }
  154. /**
  155. * 获取倒数第二个文件(夹)名
  156. * @param fullName
  157. * @param seperator
  158. * 压缩文件解压后,不同的压缩格式分隔符不一样zip是/,而rar是\
  159. * @param rootName
  160. * 根目录名:如果倒数第二个路径为空,那么赋值为rootName
  161. * @return
  162. */
  163. private static String getLast2FileName(String fullName, String seperator, String rootName) {
  164. if (fullName.endsWith(seperator)) {
  165. fullName = fullName.substring(0, fullName.length()-1);
  166. }
  167. // 1.获取剩余部分
  168. int endIndex = fullName.lastIndexOf(seperator);
  169. String leftPath = fullName.substring(0, endIndex == -1 ? 0 : endIndex);
  170. if (null != leftPath && leftPath.length() > 1) {
  171. // 2.获取倒数第二个
  172. return getLastFileName(leftPath, seperator);
  173. }else {
  174. return rootName;
  175. }
  176. }
  177. /**
  178. * 获取最后一个文件(夹)的名字
  179. * @param fullName
  180. * @param seperator
  181. * 压缩文件解压后,不同的压缩格式分隔符不一样zip是/,而rar是\
  182. * @return
  183. */
  184. private static String getLastFileName(String fullName, String seperator) {
  185. if (fullName.endsWith(seperator)) {
  186. fullName = fullName.substring(0, fullName.length()-1);
  187. }
  188. String newName = fullName;
  189. if (null != fullName && fullName.contains(seperator)) {
  190. newName = fullName.substring(fullName.lastIndexOf(seperator) + 1);
  191. }
  192. return newName;
  193. }
  194. /**
  195. * 文件节点(区分文件上下级)
  196. */
  197. public class FileNode{
  198. private String originName;
  199. private String fileName;
  200. private String parentFileName;
  201. private boolean directory;
  202. private List<FileNode> childList;
  203. public FileNode(String originName, String fileName, String parentFileName, List<FileNode> childList, boolean directory) {
  204. this.originName = originName;
  205. this.fileName = fileName;
  206. this.parentFileName = parentFileName;
  207. this.childList = childList;
  208. this.directory = directory;
  209. }
  210. public String getFileName() {
  211. return fileName;
  212. }
  213. public void setFileName(String fileName) {
  214. this.fileName = fileName;
  215. }
  216. public String getParentFileName() {
  217. return parentFileName;
  218. }
  219. public void setParentFileName(String parentFileName) {
  220. this.parentFileName = parentFileName;
  221. }
  222. public List<FileNode> getChildList() {
  223. return childList;
  224. }
  225. public void setChildList(List<FileNode> childList) {
  226. this.childList = childList;
  227. }
  228. @Override
  229. public String toString() {
  230. try {
  231. return new ObjectMapper().writeValueAsString(this);
  232. } catch (JsonProcessingException e) {
  233. e.printStackTrace();
  234. return "";
  235. }
  236. }
  237. public String getOriginName() {
  238. return originName;
  239. }
  240. public void setOriginName(String originName) {
  241. this.originName = originName;
  242. }
  243. public boolean isDirectory() {
  244. return directory;
  245. }
  246. public void setDirectory(boolean directory) {
  247. this.directory = directory;
  248. }
  249. }
  250. /**
  251. * Zip文件抽取线程
  252. */
  253. class ZipExtractorWorker implements Runnable {
  254. private List<Map<String, ZipArchiveEntry>> entriesToBeExtracted;
  255. private ZipFile zipFile;
  256. private String filePath;
  257. public ZipExtractorWorker(List<Map<String, ZipArchiveEntry>> entriesToBeExtracted, ZipFile zipFile, String filePath) {
  258. this.entriesToBeExtracted = entriesToBeExtracted;
  259. this.zipFile = zipFile;
  260. this.filePath = filePath;
  261. }
  262. @Override
  263. public void run() {
  264. System.out.println("解析压缩文件开始《《《《《《《《《《《《《《《《《《《《《《《");
  265. for (Map<String, ZipArchiveEntry> entryMap : entriesToBeExtracted) {
  266. String childName = entryMap.keySet().iterator().next();
  267. ZipArchiveEntry entry = entryMap.values().iterator().next();
  268. try {
  269. extractZipFile(childName, zipFile.getInputStream(entry));
  270. } catch (IOException e) {
  271. e.printStackTrace();
  272. }
  273. }
  274. try {
  275. zipFile.close();
  276. } catch (IOException e) {
  277. e.printStackTrace();
  278. }
  279. if (new File(filePath).exists()) {
  280. new File(filePath).delete();
  281. }
  282. System.out.println("解析压缩文件结束《《《《《《《《《《《《《《《《《《《《《《《");
  283. }
  284. /**
  285. * 读取压缩文件并写入到fileDir文件夹下
  286. * @param childName
  287. * @param zipFile
  288. */
  289. private void extractZipFile(String childName, InputStream zipFile) {
  290. String outPath = fileDir + childName;
  291. try (OutputStream ot = new FileOutputStream(outPath)){
  292. byte[] inByte = new byte[1024];
  293. int len;
  294. while ((-1 != (len = zipFile.read(inByte)))){
  295. ot.write(inByte, 0, len);
  296. }
  297. } catch (FileNotFoundException e) {
  298. e.printStackTrace();
  299. } catch (IOException e) {
  300. e.printStackTrace();
  301. }
  302. }
  303. }
  304. /**
  305. * Rar文件抽取
  306. */
  307. class RarExtractorWorker implements Runnable {
  308. private List<Map<String, FileHeader>> headersToBeExtracted;
  309. private Archive archive;
  310. /**
  311. * 用以删除源文件
  312. */
  313. private String filePath;
  314. public RarExtractorWorker(List<Map<String, FileHeader>> headersToBeExtracted, Archive archive, String filePath) {
  315. this.headersToBeExtracted = headersToBeExtracted;
  316. this.archive = archive;
  317. this.filePath = filePath;
  318. }
  319. @Override
  320. public void run() {
  321. System.out.println("解析压缩文件开始《《《《《《《《《《《《《《《《《《《《《《《");
  322. for (Map<String, FileHeader> entryMap : headersToBeExtracted) {
  323. String childName = entryMap.keySet().iterator().next();
  324. extractRarFile(childName, entryMap.values().iterator().next(), archive);
  325. }
  326. try {
  327. archive.close();
  328. } catch (IOException e) {
  329. e.printStackTrace();
  330. }
  331. if (new File(filePath).exists()) {
  332. new File(filePath).delete();
  333. }
  334. System.out.println("解析压缩文件结束《《《《《《《《《《《《《《《《《《《《《《《");
  335. }
  336. /**
  337. * 抽取rar文件到指定目录下
  338. * @param childName
  339. * @param header
  340. * @param archive
  341. */
  342. private void extractRarFile(String childName, FileHeader header, Archive archive) {
  343. String outPath = fileDir + childName;
  344. try(OutputStream ot = new FileOutputStream(outPath)) {
  345. archive.extractFile(header, ot);
  346. } catch (FileNotFoundException e) {
  347. e.printStackTrace();
  348. } catch (IOException e) {
  349. e.printStackTrace();
  350. } catch (RarException e) {
  351. e.printStackTrace();
  352. }
  353. }
  354. }
  355. }