FastExcel 实现数据分批次导入、导出

发布于:2025-03-01 ⋅ 阅读:(199) ⋅ 点赞:(0)

是基于 FastExcel 实现数据分批次导入和保存的完整解决方案,结合了高性能流式读取与分批处理机制:


一、环境准备

  1. 依赖配置

    <dependency>
        <groupId>cn.idev.excel</groupId>
        <artifactId>fastexcel</artifactId>
        <version>1.1.0</version>
    </dependency>

  2. 配置文件调整
    application.yml 中增加上传文件大小限制(适用于大文件上传):

    spring:
      servlet:
        multipart:
          max-file-size: 500MB
          max-request-size: 500MB


二、分批次导入实现

1. 实体类定义

使用 @ExcelProperty 注解映射 Excel 列:

@Data
public class DataEntity {
    @ExcelProperty("ID")
    private Long id;
    @ExcelProperty("名称")
    private String name;

}

2. 自定义监听器(核心)

通过流式读取实现内存优化和分批次处理:

public class BatchImportListener extends AnalysisEventListener<DataEntity> {
    private static final int BATCH_SIZE = 10000; // 每批次处理量
    private List<DataEntity> batchList = new ArrayList<>();
    private final DataService dataService; // 数据服务类

    @Override
    public void invoke(DataEntity data, AnalysisContext context) {
        batchList.add(data);
        if (batchList.size() >= BATCH_SIZE) {
            saveBatch(); // 达到批次阈值时保存
            batchList.clear();
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        if (!batchList.isEmpty()) saveBatch(); // 处理剩余数据
    }

    private void saveBatch() {
        dataService.batchInsert(batchList); // 调用批量插入方法
    }
}

3. 控制器调用
@PostMapping("/import")
public String importExcel(@RequestParam("file") MultipartFile file) {
    FastExcel.read(file.getInputStream(), DataEntity.class, new BatchImportListener(dataService))
             .sheet().doRead();
    return "导入成功";
}


三、分批次导出实现

1. 流式写入避免 OOM,使用临时文件缓存模式
  1. 内存优化
    默认情况下(.inMemory(true)),所有数据会暂存到内存中,当导出超过 10万行 数据时,极易引发 OutOfMemoryError
    启用 .inMemory(false) 后,数据将写入 临时文件(默认在系统临时目录),内存仅保留索引信息。

  2. 稳定性保障
    适合处理百万级甚至千万级数据导出场景,内存占用始终保持在 50MB 以内(实测数据)。

public void exportLargeData(HttpServletResponse response, String fileName) {
    response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
    response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));

    try (ExcelWriter excelWriter = FastExcel.write(response.getOutputStream(), DataEntity.class)
                                            .inMemory(false) // 关键配置:启用磁盘缓存
                                            .build()) {
        
        WriteSheet sheet = FastExcel.writerSheet("百万数据").build();
        
        int page = 1;
        while (true) {
            List<DataEntity> batch = dataService.getBatchData(page, 50000); // 每次查询5万条
            if (batch.isEmpty()) break;
            
            excelWriter.write(batch, sheet); // 分批写入临时文件
            log.info("已写入第 {} 批,累计 {} 条", page, page * 50000);
            page++;
        }
    } catch (IOException e) {
        throw new RuntimeException("导出失败", e);
    }
}
2.临时文件管理
  1. 文件位置
    临时文件路径格式:/tmp/fastexcel-cache-{UUID}.tmp(Linux/Mac)或 C:\Users\xxx\AppData\Local\Temp\fastexcel-cache-{UUID}.tmp(Windows)

  2. 自动清理
    FastExcel 在以下情况会自动删除临时文件:

    • 调用 ExcelWriter.close() 时
    • JVM 正常退出时
    • 可通过 excelWriter.cleanup() 手动强制清理


四、关键优化点

  1. 内存控制

    • 导出时使用 try-with-resources 确保资源释放 
  2. 性能提升

    • 数据库操作使用批量插入而非单条插入(速度提升 10 倍+)。
    • 导出时启用临时文件缓存模式(.inMemory(false))。
  3. 异常处理

    • 在监听器中添加事务边界管理,避免长事务问题。
    • 导出文件名添加时间戳防重复:fileName + System.currentTimeMillis() + ".xlsx"

五、注意事项

  1. 大文件处理时建议增加进度提示(如每批次打印日志) 
  2. 导出时若需追加数据,应复用同一个 ExcelWriter 对象 
  3. 事务注解 @Transactional 建议加在 Service 方法而非监听器 

网站公告

今日签到

点亮在社区的每一天
去签到