yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
@@ -1,9 +1,16 @@
package cn.iocoder.yudao.framework.excel.core.util;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcelFactory;
import cn.idev.excel.converters.longconverter.LongStringConverter;
import cn.idev.excel.exception.ExcelAnalysisException;
import cn.idev.excel.exception.ExcelCommonException;
import cn.idev.excel.exception.ExcelDataConvertException;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import lombok.extern.slf4j.Slf4j;
import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
import cn.iocoder.yudao.framework.excel.core.handler.DateAsTextCellWriteHandler;
import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
@@ -14,11 +21,15 @@
import java.io.InputStream;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.excel.core.enums.ExcelErrorCodeConstants.*;
/**
 * Excel 工具类
 *
 * @author 超级管理员
 */
@Slf4j
public class ExcelUtils {
    /**
@@ -82,13 +93,61 @@
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
    }
    public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
    /**
     * 读取 Excel 文件
     *
     * <p>所有导入接口都经过这里,因此在这里统一把「坏文件 / 空文件 / 模板不匹配 / 单元格格式错」
     * 等各种底层异常,转换成可读的中文提示(ServiceException)。
     * 这样业务接口不必各自写 try-catch,也不会再出现含糊的「系统异常」500。</p>
     *
     * @param file Excel 文件
     * @param head 表头对应的 VO 类
     * @return 解析结果
     */
    public static <T> List<T> read(MultipartFile file, Class<T> head) {
        // 1. 文件本身为空
        if (file == null || file.isEmpty()) {
            throw exception(EXCEL_FILE_EMPTY);
        }
        // 2. 解析(逐类识别异常,给出可读提示)
        List<T> list;
        // 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景
        try (InputStream inputStream = file.getInputStream()) {
            return FastExcelFactory.read(inputStream, head, null)
            list = FastExcelFactory.read(inputStream, head, null)
                    .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
                    .doReadAllSync();
        } catch (IOException ex) {
            log.warn("[read][Excel 文件读取失败,文件名({})]", file.getOriginalFilename(), ex);
            throw exception(EXCEL_FILE_READ_FAILED);
        } catch (ExcelDataConvertException ex) {
            // 单元格数据格式不对,例如文本列填了数字、日期列填了乱码
            String detail = StrUtil.blankToDefault(ex.getMessage(), "内容与列类型不匹配");
            log.warn("[read][Excel 单元格数据格式错误,行({}) 列({})]", ex.getRowIndex(), ex.getColumnIndex(), ex);
            throw exception(EXCEL_DATA_CONVERT_FAILED,
                    (ex.getRowIndex() == null ? 0 : ex.getRowIndex()) + 1,
                    (ex.getColumnIndex() == null ? 0 : ex.getColumnIndex()) + 1,
                    StrUtil.maxLength(detail, 60));
        } catch (ExcelAnalysisException ex) {
            // 文件损坏、加密、不是真正的 Excel、表头与模板对不上等
            String rootMsg = StrUtil.blankToDefault(ExceptionUtil.getRootCauseMessage(ex), "");
            log.warn("[read][Excel 解析失败,文件名({}),原因({})]", file.getOriginalFilename(), rootMsg, ex);
            if (StrUtil.containsAnyIgnoreCase(rootMsg, "head", "Can not find", "autoTrim", "Convert")) {
                throw exception(EXCEL_HEAD_NOT_MATCH);
        }
            throw exception(EXCEL_TEMPLATE_NOT_MATCH);
        } catch (ExcelCommonException ex) {
            log.warn("[read][Excel 通用异常,文件名({})]", file.getOriginalFilename(), ex);
            throw exception(EXCEL_FILE_READ_FAILED);
        } catch (Exception ex) {
            // 兜底:任何未预料的解析异常都转成可读提示,避免直接 500「系统异常」
            log.warn("[read][Excel 解析出现未预期异常,文件名({})]", file.getOriginalFilename(), ex);
            throw exception(EXCEL_FILE_READ_FAILED);
        }
        // 3. 解析出来是空表(只有表头、没有数据行)
        if (CollUtil.isEmpty(list)) {
            throw exception(EXCEL_FILE_EMPTY_CONTENT);
        }
        return list;
    }
}