PreTask.java 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. package com.qingjian.module.dm.pre;
  2. import cn.hutool.core.io.FileUtil;
  3. import cn.hutool.core.io.IoUtil;
  4. import cn.hutool.core.util.CharsetUtil;
  5. import cn.hutool.core.util.IdUtil;
  6. import com.qingjian.common.enums.DelimiterEnum;
  7. import com.qingjian.common.enums.FileSheetStateEnum;
  8. import com.qingjian.common.model.dm.entity.FileInfo;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.apache.commons.csv.CSVFormat;
  11. import org.apache.fesod.sheet.ExcelReader;
  12. import org.apache.fesod.sheet.FesodSheet;
  13. import org.apache.fesod.sheet.cache.selector.SimpleReadCacheSelector;
  14. import org.apache.fesod.sheet.read.metadata.ReadWorkbook;
  15. import org.apache.fesod.sheet.read.metadata.holder.ReadWorkbookHolder;
  16. import org.apache.fesod.sheet.read.metadata.holder.csv.CsvReadWorkbookHolder;
  17. import org.apache.fesod.sheet.support.ExcelTypeEnum;
  18. import org.apache.poi.EncryptedDocumentException;
  19. import org.apache.poi.UnsupportedFileFormatException;
  20. import org.apache.poi.ooxml.POIXMLException;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.nio.charset.Charset;
  24. import java.nio.charset.StandardCharsets;
  25. import java.nio.file.Files;
  26. import java.nio.file.Path;
  27. import java.time.LocalDateTime;
  28. import java.util.ArrayList;
  29. import java.util.List;
  30. import java.util.Objects;
  31. /**
  32. * 文件预处理任务
  33. * 负责读取 Excel/CSV 文件,检测文件类型和编码,启动预览数据解析流程
  34. * 支持加密文件处理和自定义分隔符配置
  35. */
  36. @Slf4j
  37. public class PreTask {
  38. /**
  39. * 文件路径
  40. */
  41. private final Path filePath;
  42. /**
  43. * 批次 ID,同一批次的文件共享相同 ID
  44. */
  45. private final Long batchId;
  46. /**
  47. * 文件名
  48. */
  49. private final String fileName;
  50. /**
  51. * 文件密码,用于处理加密的 Excel 文件
  52. */
  53. private final String password;
  54. /**
  55. * CSV 分隔符,仅 CSV 文件使用
  56. */
  57. private final Character delimiter;
  58. /**
  59. * 文件内容 MD5,用于去重和落库保存
  60. */
  61. private final String md5;
  62. /**
  63. * 构造函数
  64. *
  65. * @param filePath 文件路径
  66. * @param batchId 批次 ID
  67. * @param password 文件密码
  68. * @param delimiter CSV 分隔符,默认使用逗号
  69. * @param md5 文件内容 MD5
  70. */
  71. public PreTask(Path filePath, Long batchId, String password, Character delimiter, String md5) {
  72. this.filePath = filePath;
  73. this.batchId = batchId;
  74. this.fileName = FileUtil.getName(filePath);
  75. this.password = password;
  76. this.delimiter = Objects.requireNonNullElse(delimiter, ',');
  77. this.md5 = md5;
  78. }
  79. /**
  80. * 执行文件预处理任务
  81. * 检测文件类型和编码,解析文件结构,提取预览数据
  82. *
  83. * @return 文件信息列表,包含主文件和所有工作表的元数据
  84. */
  85. public List<FileInfo> call() {
  86. final List<FileInfo> fileInfoList = new ArrayList<>();
  87. final FileInfo fileInfo = new FileInfo();
  88. // 初始化文件基本信息
  89. initDefaultFileInfo(fileInfo);
  90. Long fileId = IdUtil.getSnowflakeNextId();
  91. fileInfo.setId(fileId);
  92. fileInfo.setFilePath(this.filePath.toString());
  93. fileInfo.setMd5(md5);
  94. fileInfoList.add(fileInfo);
  95. // 默认使用 UTF-8 编码
  96. Charset charset = StandardCharsets.UTF_8;
  97. ExcelTypeEnum excelTypeEnum;
  98. try {
  99. // 读取文件流并检测文件类型
  100. InputStream inputStream = Files.newInputStream(filePath);
  101. ReadWorkbook readWorkbook = new ReadWorkbook();
  102. readWorkbook.setInputStream(inputStream);
  103. excelTypeEnum = ExcelTypeEnum.valueOf(readWorkbook);
  104. IoUtil.close(inputStream);
  105. // CSV 文件需要检测实际编码
  106. fileInfo.setFileType(excelTypeEnum.name());
  107. if (excelTypeEnum.equals(ExcelTypeEnum.CSV)) {
  108. Charset newCharset = CharsetUtil.defaultCharset(Files.newInputStream(filePath));
  109. if (!newCharset.equals(StandardCharsets.UTF_16BE)) {
  110. charset = newCharset;
  111. }
  112. }
  113. } catch (IOException e) {
  114. log.error(e.getMessage(), e);
  115. return fileInfoList;
  116. }
  117. // 设置文件编码和密码
  118. fileInfo.setCharSet(charset.displayName());
  119. fileInfo.setPassword(password);
  120. fileInfo.setDelimiter(DelimiterEnum.getEnum(delimiter));
  121. fileInfo.setFileStatus(FileSheetStateEnum.SHEET_TEMPLATE_MATCHED_FAIL);
  122. // 创建预览数据监听器并开始解析
  123. PreDataListener preDataListener = new PreDataListener(fileInfoList, fileName, fileId, batchId);
  124. try (ExcelReader reader = FesodSheet.read(Files.newInputStream(filePath), preDataListener)
  125. .readCacheSelector(new SimpleReadCacheSelector(20L, 500))
  126. .password(password).headRowNumber(0).excelType(excelTypeEnum)
  127. .autoTrim(true).charset(charset).registerConverter(new Str2NullConverter())
  128. .registerConverter(new DateNumberConverter()).build()) {
  129. ReadWorkbookHolder readWorkbookHolder = reader.analysisContext().readWorkbookHolder();
  130. // 判断只有 csv 才需要修改配置信息
  131. if (readWorkbookHolder instanceof CsvReadWorkbookHolder csvReadWorkbookHolder) {
  132. // 底层读取 csv 用的是 apache 的 common-csv,所以设置 CsvFormat 即可
  133. csvReadWorkbookHolder.setCsvFormat(CSVFormat.DEFAULT.builder().setDelimiter(delimiter).get());
  134. }
  135. // 开始解析所有工作表
  136. reader.readAll();
  137. } catch (Exception e) {
  138. log.error(e.getMessage(), e);
  139. // 不支持的文件格式
  140. if (e instanceof UnsupportedFileFormatException || e instanceof POIXMLException) {
  141. fileInfo.setFileStatus(FileSheetStateEnum.FILE_UNSUPPORTED_FAIL);
  142. fileInfo.setFileType("unsupported");
  143. }
  144. // 加密文件异常
  145. if (e instanceof EncryptedDocumentException) {
  146. fileInfo.setFileStatus(FileSheetStateEnum.FILE_ENCRYPTION);
  147. }
  148. }
  149. fileInfo.setFileStatus(FileSheetStateEnum.FILE_READ_COMPLETE);
  150. return fileInfoList;
  151. }
  152. /**
  153. * 初始化文件基本信息
  154. * 设置默认的文件类型、状态、时间戳等
  155. *
  156. * @param fileInfo 待初始化的文件信息对象
  157. */
  158. private void initDefaultFileInfo(FileInfo fileInfo) {
  159. fileInfo.setFileType(ExcelTypeEnum.XLS.name());
  160. fileInfo.setPid(0L);
  161. fileInfo.setFileName(fileName);
  162. fileInfo.setDataNum(0);
  163. fileInfo.setSuccessNum(0);
  164. fileInfo.setFailNum(0);
  165. fileInfo.setFileSize(0L);
  166. fileInfo.setBatchId(batchId);
  167. fileInfo.setFileStatus(FileSheetStateEnum.FILE_READ_FAIL);
  168. fileInfo.setCreateDateTime(LocalDateTime.now());
  169. }
  170. }