文档读取Excel复杂读取

复杂读取 Excel 文件指南

概述

本指南介绍如何使用 FastExcel 完成多场景 Excel 复杂读取。涉及的场景包括:按列名或下标读取、多 Sheet 读取、日期和数字格式转换、多行表头解析、读取额外信息(如批注、超链接、合并单元格)、处理公式和异常等。

按列名或列下标读取

概述

您可以通过指定列名或列下标来读取 Excel 数据。这使得与动态生成的 Excel 文件交互更加灵活。

示例代码

示例对象:IndexOrNameData

@Getter
@Setter
@EqualsAndHashCode
public class IndexOrNameData {
    @ExcelProperty(index = 2)
    private Double doubleData;
 
    @ExcelProperty("字符串标题")
    private String string;
 
    @ExcelProperty("日期标题")
    private Date date;
}

监听器

与基础读取相同,仅需要修改泛型为 IndexOrNameData

读取代码示例

@Test
public void indexOrNameRead() {
    String fileName = "path/to/demo.xlsx";
 
    // 指定列名或列下标读取
    FastExcel.read(fileName, IndexOrNameData.class, new DemoDataListener()).sheet().doRead();
}

读取多个 Sheet

概述

可以读取 Excel 文件中的多个 Sheet,且同一个 Sheet 不可重复读取。

示例对象与监听器

与基础读取相同。

读取代码示例

@Test
public void repeatedRead() {
    String fileName = "path/to/demo.xlsx";
 
    // 读取全部 Sheet
    FastExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
 
    // 读取指定 Sheet
    try (ExcelReader excelReader = FastExcel.read(fileName).build()) {
        ReadSheet sheet1 = FastExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
        ReadSheet sheet2 = FastExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
        excelReader.read(sheet1, sheet2);
    }
}

日期、数字、自定义格式转换

示例对象:ConverterData

@Getter
@Setter
@EqualsAndHashCode
public class ConverterData {
    @ExcelProperty(converter = CustomStringStringConverter.class)
    private String string;
 
    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    private String date;
 
    @NumberFormat("#.##%")
    private String doubleData;
}

自定义转换器

public class CustomStringStringConverter implements Converter<String> {
    @Override
    public Class<?> supportJavaTypeKey() {
        return String.class;
    }
 
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }
 
    @Override
    public String convertToJavaData(ReadConverterContext<?> context) {
        return "自定义:" + context.getReadCellData().getStringValue();
    }
 
    @Override
    public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
        return new WriteCellData<>(context.getValue());
    }
}

读取代码示例

@Test
public void converterRead() {
    String fileName = "path/to/demo.xlsx";
 
    // 自定义格式读取
    FastExcel.read(fileName, ConverterData.class, new DemoDataListener())
        .registerConverter(new CustomStringStringConverter())
        .sheet().doRead();
}

多行表头读取

概述

通过设置 headRowNumber 参数或根据实体类的表头注解自动解析多行表头。

读取代码示例

@Test
public void complexHeaderRead() {
    String fileName = "path/to/demo.xlsx";
 
    FastExcel.read(fileName, DemoData.class, new DemoDataListener())
        .sheet()
        // 设置多行表头的行数,默认为 1
        .headRowNumber(2)
        .doRead();
}

同步返回数据

概述

同步读取 Excel 数据,直接返回数据列表。适用于小数据量场景,不推荐大数据量使用。

读取代码示例

@Test
public void synchronousRead() {
    String fileName = "path/to/demo.xlsx";
 
    // 返回实体对象的列表
    List<DemoData> list = FastExcel.read(fileName).head(DemoData.class).sheet().doReadSync();
    for (DemoData data : list) {
        log.info("读取到数据: {}", JSON.toJSONString(data));
    }
 
    // 返回 Map 列表,键为列索引,值为单元格内容
    List<Map<Integer, String>> listMap = FastExcel.read(fileName).sheet().doReadSync();
    for (Map<Integer, String> data : listMap) {
        log.info("读取到数据: {}", JSON.toJSONString(data));
    }
}

读取表头数据

概述

可以通过重写监听器的 invokeHead 方法获取表头信息。

示例监听器

@Slf4j
public class DemoHeadDataListener extends AnalysisEventListener<DemoData> {
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        log.info("解析到表头数据: {}", JSON.toJSONString(headMap));
    }
 
    @Override
    public void invoke(DemoData data, AnalysisContext context) {}
 
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {}
}

读取代码示例

@Test
public void headerRead() {
    String fileName = "path/to/demo.xlsx";
    FastExcel.read(fileName, DemoData.class, new DemoHeadDataListener()).sheet().doRead();
}

额外信息读取(批注、超链接、合并单元格)

概述

通过设置 extraRead 参数读取额外信息,如批注、超链接、合并单元格。

示例监听器

@Slf4j
public class DemoExtraListener implements ReadListener<DemoExtraData> {
    @Override
    public void invoke(DemoExtraData data, AnalysisContext context) {}
 
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {}
 
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        log.info("读取到额外信息: {}", JSON.toJSONString(extra));
        switch (extra.getType()) {
            case COMMENT:
                log.info("批注信息: {}", extra.getText());
                break;
            case HYPERLINK:
                log.info("超链接信息: {}", extra.getText());
                break;
            case MERGE:
                log.info("合并单元格范围: {} - {}", extra.getFirstRowIndex(), extra.getLastRowIndex());
                break;
            default:
                log.warn("未知的额外信息类型");
        }
    }
}

读取代码示例

@Test
public void extraRead() {
    String fileName = "path/to/demo.xlsx";
 
    FastExcel.read(fileName, DemoExtraData.class, new DemoExtraListener())
        .extraRead(CellExtraTypeEnum.COMMENT) // 读取批注
        .extraRead(CellExtraTypeEnum.HYPERLINK) // 读取超链接
        .extraRead(CellExtraTypeEnum.MERGE) // 读取合并单元格
        .sheet().doRead();
}

读取公式和单元格类型

概述

使用 CellData 类型接收单元格数据以支持公式和多种单元格格式。

示例对象

@Getter
@Setter
@EqualsAndHashCode
public class CellDataReadDemoData {
    private CellData<String> string;
    private CellData<Date> date;
    private CellData<Double> doubleData;
    private CellData<String> formulaValue;
}

读取代码示例

@Test
public void cellDataRead() {
    String fileName = "path/to/demo.xlsx";
 
    FastExcel.read(fileName, CellDataReadDemoData.class, new DemoDataListener()).sheet().doRead();
}

异常处理

概述

通过重写监听器的 onException 方法处理数据转换或其他读取异常。

示例监听器

@Slf4j
public class DemoExceptionListener extends AnalysisEventListener<ExceptionDemoData> {
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        log.error("解析失败: {}", exception.getMessage());
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException ex = (ExcelDataConvertException) exception;
            log.error("第 {} 行, 第 {} 列解析异常, 数据: {}", ex.getRowIndex(), ex.getColumnIndex(), ex.getCellData());
        }
    }
 
    @Override
    public void invoke(ExceptionDemoData data, AnalysisContext context) {}
 
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {}
}

读取代码示例

@Test
public void exceptionRead() {
    String fileName = "path/to/demo.xlsx";
    FastExcel.read(fileName, ExceptionDemoData.class, new DemoExceptionListener()).sheet().doRead();
}

不创建对象的读取

概述

通过 Map<Integer, String> 直接读取数据,而无需定义实体类。

示例监听器

@Slf4j
public class NoModelDataListener extends AnalysisEventListener<Map<Integer, String>> {
    private static final int BATCH_COUNT = 5;
    private List<Map<Integer, String>> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
 
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        log.info("解析到一条数据: {}", JSON.toJSONString(data));
        cachedDataList.add(data);
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            cachedDataList.clear();
        }
    }
 
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();
    }
 
    private void saveData() {
        log.info("存储 {} 条数据", cachedDataList.size());
    }
}

读取代码示例

@Test
public void noModelRead() {
    String fileName = "path/to/demo.xlsx";
    FastExcel.read(fileName, new NoModelDataListener()).sheet().doRead();
}