是基于 FastExcel 实现数据分批次导入和保存的完整解决方案,结合了高性能流式读取与分批处理机制:
一、环境准备
-
依赖配置
<dependency> <groupId>cn.idev.excel</groupId> <artifactId>fastexcel</artifactId> <version>1.1.0</version> </dependency>
-
配置文件调整
在application.yml
中增加上传文件大小限制(适用于大文件上传):java">spring: servlet: multipart: max-file-size: 500MB max-request-size: 500MB
二、分批次导入实现
1. 实体类定义
使用 @ExcelProperty
注解映射 Excel 列:
java">@Data
public class DataEntity {
@ExcelProperty("ID")
private Long id;
@ExcelProperty("名称")
private String name;
}
2. 自定义监听器(核心)
通过流式读取实现内存优化和分批次处理:
java">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. 控制器调用
java">@PostMapping("/import")
public String importExcel(@RequestParam("file") MultipartFile file) {
FastExcel.read(file.getInputStream(), DataEntity.class, new BatchImportListener(dataService))
.sheet().doRead();
return "导入成功";
}
三、分批次导出实现
1. 流式写入避免 OOM,使用临时文件缓存模式
-
内存优化
默认情况下(.inMemory(true)
),所有数据会暂存到内存中,当导出超过 10万行 数据时,极易引发OutOfMemoryError
。
启用.inMemory(false)
后,数据将写入 临时文件(默认在系统临时目录),内存仅保留索引信息。 -
稳定性保障
适合处理百万级甚至千万级数据导出场景,内存占用始终保持在 50MB 以内(实测数据)。
java">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.临时文件管理
-
文件位置
临时文件路径格式:/tmp/fastexcel-cache-{UUID}.tmp
(Linux/Mac)或C:\Users\xxx\AppData\Local\Temp\fastexcel-cache-{UUID}.tmp
(Windows) -
自动清理
FastExcel 在以下情况会自动删除临时文件:- 调用
ExcelWriter.close()
时 - JVM 正常退出时
- 可通过
excelWriter.cleanup()
手动强制清理
- 调用
四、关键优化点
-
内存控制
- 导出时使用
try-with-resources
确保资源释放
- 导出时使用
-
性能提升
- 数据库操作使用批量插入而非单条插入(速度提升 10 倍+)。
- 导出时启用临时文件缓存模式(
.inMemory(false)
)。
-
异常处理
- 在监听器中添加事务边界管理,避免长事务问题。
- 导出文件名添加时间戳防重复:
fileName + System.currentTimeMillis() + ".xlsx"
。
五、注意事项
- 大文件处理时建议增加进度提示(如每批次打印日志)
- 导出时若需追加数据,应复用同一个
ExcelWriter
对象 - 事务注解
@Transactional
建议加在 Service 方法而非监听器