EasyExcel 动态列映射读取常见异常处理与解决方案

EasyExcel 动态列映射读取常见异常处理与解决方案

1. 列名不匹配异常

现象:读取时抛出 ExcelAnalysisException,提示 "Can not find header"
原因:动态列名与Java对象字段注解不匹配
解决方案

public class DynamicData {
    @ExcelProperty("动态列_*") // 通配符匹配列名前缀
    private Map<String, String> dynamicColumns;
}

处理逻辑

  • 使用 @ExcelProperty 注解配合通配符 *
  • 通过 Map 接收动态列数据
  • 确保Excel列名符合正则匹配规则

2. 数据类型转换异常

现象:数字/日期格式解析失败,抛出 DataConvertException
原因:Excel数据类型与Java字段类型不兼容
解决方案

public class CustomConverter implements Converter<LocalDateTime> {
    @Override
    public LocalDateTime convert(String value) {
        return LocalDateTime.parse(value, DateTimeFormatter.ISO_DATE);
    }
}

@ExcelProperty(value = "日期列", converter = CustomConverter.class)
private LocalDateTime dateColumn;

处理要点

  1. 实现 Converter 接口自定义转换逻辑
  2. 处理空值:if(value == null) return null;
  3. 添加格式校验:try-catch 捕获格式异常

3. 动态列缺失异常

现象:部分动态列未读取到数据
原因:列位置变化导致映射失效
解决方案

ReadSheet readSheet = EasyExcel.readSheet()
    .headRowNumber(2) // 设置表头行
    .registerReadListener(new AnalysisEventListener() {
        @Override
        public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
            // 动态获取列索引
            columnIndex = headMap.entrySet().stream()
                .filter(e -> e.getValue().contains("动态列"))
                .findFirst()
                .map(Map.Entry::getKey);
        }
    });

优化策略

  • 使用 headMap 动态获取列索引
  • 添加默认值逻辑:columnIndex.orElse(0)

4. 大数据量内存溢出

现象:读取大文件时 OutOfMemoryError
原因:默认全量加载数据到内存
解决方案

// 分页读取配置
ReadSheet readSheet = EasyExcel.readSheet()
    .head(DynamicData.class)
    .registerReadListener(new PageReadListener(dataList -> {
        processBatch(dataList); // 分批处理
        dataList.clear();       // 清空缓存
    }, 1000));                 // 每1000行处理一次

关键参数

  • PageReadListener 分批处理数据
  • 设置JVM参数:-Xms512m -Xmx2g
  • 使用 SXSSFWorkbook 模式

5. 多级表头解析异常

现象:复杂表头数据映射错误
解决方案

@ExcelProperty({"主标题", "子标题"}) // 多级表头声明
private String nestedColumn;

// 读取配置
EasyExcel.read(file)
    .extraRead(CacheExtra.class) // 启用缓存
    .headRowNumber(3)            // 表头占3行
    .doRead();

处理技巧

  1. 使用数组声明多级表头:@ExcelProperty({"一级", "二级"})
  2. 调整 headRowNumber 匹配实际表头行数
  3. 通过 @ExcelIgnoreUnannotated 忽略未注解字段

综合处理方案
public class DynamicReadHandler {
    public void readExcel(MultipartFile file) {
        EasyExcel.read(file.getInputStream())
            .registerConverter(new CustomConverter()) // 注册转换器
            .registerReadListener(new DynamicHeadListener()) // 动态列处理器
            .head(DynamicData.class)
            .sheet()
            .doRead();
    }

    private static class DynamicHeadListener extends AnalysisEventListener<DynamicData> {
        private final Map<String, Integer> columnIndexMap = new HashMap<>();

        @Override
        public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
            headMap.forEach((index, name) -> 
                columnIndexMap.put(name, index));
        }

        @Override
        public void invoke(DynamicData data, AnalysisContext context) {
            // 动态处理列数据
            Integer index = columnIndexMap.get("动态列");
            if(index != null) {
                data.setDynamicValue(context.readRowHolder().getCell(index));
            }
        }
    }
}

最佳实践

  1. 使用 @ExcelIgnore 忽略不需要的列
  2. 添加异常重试机制:
try {
    excelReader.read();
} catch (ExcelAnalysisException e) {
    log.error("解析失败行: {}", e.getRowIndex());
    // 跳过错误行继续读取
    context.readRowHolder().next(); 
}

  1. 通过 ReadCellData 获取原始数据:
ReadCellData<?> cellData = context.readRowHolder().getCell(0);
Object value = cellData.getData(); // 原始值
CellDataTypeEnum type = cellData.getType(); // 数据类型

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值