| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package com.qingjian.module.dm.pre;
- import cn.hutool.core.io.FileUtil;
- import cn.hutool.core.io.IoUtil;
- import cn.hutool.core.util.CharsetUtil;
- import cn.hutool.core.util.IdUtil;
- import com.qingjian.common.enums.DelimiterEnum;
- import com.qingjian.common.enums.FileSheetStateEnum;
- import com.qingjian.common.model.dm.entity.FileInfo;
- import lombok.extern.slf4j.Slf4j;
- import org.apache.commons.csv.CSVFormat;
- import org.apache.fesod.sheet.ExcelReader;
- import org.apache.fesod.sheet.FesodSheet;
- import org.apache.fesod.sheet.cache.selector.SimpleReadCacheSelector;
- import org.apache.fesod.sheet.read.metadata.ReadWorkbook;
- import org.apache.fesod.sheet.read.metadata.holder.ReadWorkbookHolder;
- import org.apache.fesod.sheet.read.metadata.holder.csv.CsvReadWorkbookHolder;
- import org.apache.fesod.sheet.support.ExcelTypeEnum;
- import org.apache.poi.EncryptedDocumentException;
- import org.apache.poi.UnsupportedFileFormatException;
- import org.apache.poi.ooxml.POIXMLException;
- import java.io.IOException;
- import java.io.InputStream;
- import java.nio.charset.Charset;
- import java.nio.charset.StandardCharsets;
- import java.nio.file.Files;
- import java.nio.file.Path;
- import java.time.LocalDateTime;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Objects;
- /**
- * 文件预处理任务
- * 负责读取 Excel/CSV 文件,检测文件类型和编码,启动预览数据解析流程
- * 支持加密文件处理和自定义分隔符配置
- */
- @Slf4j
- public class PreTask {
- /**
- * 文件路径
- */
- private final Path filePath;
- /**
- * 批次 ID,同一批次的文件共享相同 ID
- */
- private final Long batchId;
- /**
- * 文件名
- */
- private final String fileName;
- /**
- * 文件密码,用于处理加密的 Excel 文件
- */
- private final String password;
- /**
- * CSV 分隔符,仅 CSV 文件使用
- */
- private final Character delimiter;
- /**
- * 文件内容 MD5,用于去重和落库保存
- */
- private final String md5;
- /**
- * 构造函数
- *
- * @param filePath 文件路径
- * @param batchId 批次 ID
- * @param password 文件密码
- * @param delimiter CSV 分隔符,默认使用逗号
- * @param md5 文件内容 MD5
- */
- public PreTask(Path filePath, Long batchId, String password, Character delimiter, String md5) {
- this.filePath = filePath;
- this.batchId = batchId;
- this.fileName = FileUtil.getName(filePath);
- this.password = password;
- this.delimiter = Objects.requireNonNullElse(delimiter, ',');
- this.md5 = md5;
- }
- /**
- * 执行文件预处理任务
- * 检测文件类型和编码,解析文件结构,提取预览数据
- *
- * @return 文件信息列表,包含主文件和所有工作表的元数据
- */
- public List<FileInfo> call() {
- final List<FileInfo> fileInfoList = new ArrayList<>();
- final FileInfo fileInfo = new FileInfo();
- // 初始化文件基本信息
- initDefaultFileInfo(fileInfo);
- Long fileId = IdUtil.getSnowflakeNextId();
- fileInfo.setId(fileId);
- fileInfo.setFilePath(this.filePath.toString());
- fileInfo.setMd5(md5);
- fileInfoList.add(fileInfo);
- // 默认使用 UTF-8 编码
- Charset charset = StandardCharsets.UTF_8;
- ExcelTypeEnum excelTypeEnum;
- try {
- // 读取文件流并检测文件类型
- InputStream inputStream = Files.newInputStream(filePath);
- ReadWorkbook readWorkbook = new ReadWorkbook();
- readWorkbook.setInputStream(inputStream);
- excelTypeEnum = ExcelTypeEnum.valueOf(readWorkbook);
- IoUtil.close(inputStream);
- // CSV 文件需要检测实际编码
- fileInfo.setFileType(excelTypeEnum.name());
- if (excelTypeEnum.equals(ExcelTypeEnum.CSV)) {
- Charset newCharset = CharsetUtil.defaultCharset(Files.newInputStream(filePath));
- if (!newCharset.equals(StandardCharsets.UTF_16BE)) {
- charset = newCharset;
- }
- }
- } catch (IOException e) {
- log.error(e.getMessage(), e);
- return fileInfoList;
- }
- // 设置文件编码和密码
- fileInfo.setCharSet(charset.displayName());
- fileInfo.setPassword(password);
- fileInfo.setDelimiter(DelimiterEnum.getEnum(delimiter));
- fileInfo.setFileStatus(FileSheetStateEnum.SHEET_TEMPLATE_MATCHED_FAIL);
- // 创建预览数据监听器并开始解析
- PreDataListener preDataListener = new PreDataListener(fileInfoList, fileName, fileId, batchId);
- try (ExcelReader reader = FesodSheet.read(Files.newInputStream(filePath), preDataListener)
- .readCacheSelector(new SimpleReadCacheSelector(20L, 500))
- .password(password).headRowNumber(0).excelType(excelTypeEnum)
- .autoTrim(true).charset(charset).registerConverter(new Str2NullConverter())
- .registerConverter(new DateNumberConverter()).build()) {
- ReadWorkbookHolder readWorkbookHolder = reader.analysisContext().readWorkbookHolder();
- // 判断只有 csv 才需要修改配置信息
- if (readWorkbookHolder instanceof CsvReadWorkbookHolder csvReadWorkbookHolder) {
- // 底层读取 csv 用的是 apache 的 common-csv,所以设置 CsvFormat 即可
- csvReadWorkbookHolder.setCsvFormat(CSVFormat.DEFAULT.builder().setDelimiter(delimiter).get());
- }
- // 开始解析所有工作表
- reader.readAll();
- } catch (Exception e) {
- log.error(e.getMessage(), e);
- // 不支持的文件格式
- if (e instanceof UnsupportedFileFormatException || e instanceof POIXMLException) {
- fileInfo.setFileStatus(FileSheetStateEnum.FILE_UNSUPPORTED_FAIL);
- fileInfo.setFileType("unsupported");
- }
- // 加密文件异常
- if (e instanceof EncryptedDocumentException) {
- fileInfo.setFileStatus(FileSheetStateEnum.FILE_ENCRYPTION);
- }
- }
- fileInfo.setFileStatus(FileSheetStateEnum.FILE_READ_COMPLETE);
- return fileInfoList;
- }
- /**
- * 初始化文件基本信息
- * 设置默认的文件类型、状态、时间戳等
- *
- * @param fileInfo 待初始化的文件信息对象
- */
- private void initDefaultFileInfo(FileInfo fileInfo) {
- fileInfo.setFileType(ExcelTypeEnum.XLS.name());
- fileInfo.setPid(0L);
- fileInfo.setFileName(fileName);
- fileInfo.setDataNum(0);
- fileInfo.setSuccessNum(0);
- fileInfo.setFailNum(0);
- fileInfo.setFileSize(0L);
- fileInfo.setBatchId(batchId);
- fileInfo.setFileStatus(FileSheetStateEnum.FILE_READ_FAIL);
- fileInfo.setCreateDateTime(LocalDateTime.now());
- }
- }
|