Spring Boot 集成 EasyExcel 实现海量数据导入导出,核心是解决内存占用、解析/生成效率、异常容错和用户友好反馈问题;基于 SAX 解析避免 OOM,支持流式导出与容错式导入。

在 Spring Boot 中集成 EasyExcel 实现海量数据导入导出,核心是解决内存占用、解析/生成效率、异常容错和用户友好反馈四个问题。EasyExcel 基于 SAX 解析 Excel,避免了传统 POI 的 DOM 方式导致的 OOM 风险,特别适合百万级数据场景。
添加依赖与基础配置
使用 Maven 引入 EasyExcel(推荐 3.x 版本,兼容 Spring Boot 2.7+ / 3.x):
<dependency><groupid>com.alibaba</groupid><artifactid>easyexcel</artifactid><version>3.3.2</version></dependency>
无需额外配置类,但建议在 application.yml 中预留文件上传限制(尤其导出大文件或导入超大 Excel):
spring:
servlet:
context-path: /api
servlet:
multipart:
max-file-size: 50MB
max-request-size: 50MB
定义数据模型并标注注解
实体类需用 @ExcelProperty 映射列名,支持中文列头、多级属性、忽略字段等:
例如导出用户列表:
public class UserExportDTO {
@ExcelProperty("用户ID")
private Long id;
@ExcelProperty("用户名")
private String name;
@ExcelProperty("邮箱")
private String email;
@ExcelProperty("注册时间")
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
// getter/setter 省略
}
导入时若需跳过空行、校验格式或处理下拉选值,可配合 @ExcelIgnore、自定义 Converter 或全局 Converter 实现。
实现流式导出(避免内存溢出)
不把全部数据加载进内存,而是分页查库 + 写入响应流:
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- Controller 返回
ResponseEntity<resource></resource>或直接写入HttpServletResponse.getOutputStream() - 使用
ExcelWriter+WriteSheet,配合SimpleWriterBuilder设置自动关闭流 - 推荐分批写入(如每 1 万条 flush 一次),提升响应速度和稳定性
示例片段(导出用户):
@GetMapping("/export")
public void exportUsers(HttpServletResponse response) throws IOException {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
String fileName = URLEncoder.encode("用户列表", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelWriter writer = EasyExcel.write(response.getOutputStream(), UserExportDTO.class).build();
WriteSheet sheet = EasyExcel.writerSheet("用户数据").build();
int pageSize = 10000;
int pageNo = 1;
List<userexportdto> data;
do {
data = userService.findPageForExport(pageNo++, pageSize);
if (!data.isEmpty()) {
writer.write(data, sheet);
}
} while (data.size() == pageSize);
writer.finish();
}</userexportdto>
实现容错式导入(支持百万行+校验)
使用 AnalysisEventListener 实现异步解析 + 批量入库,避免单次事务过大:
- 重写
invoke方法逐行接收数据,缓存到 List,达到阈值后批量插入 DB - 重写
doAfterAllAnalysed处理剩余数据、发送结果通知、清理资源 - 在
onException中捕获解析异常,记录错误行号和原因,返回给前端供下载错误报告
关键点:
- 数据库插入建议用 MyBatis-Plus 的
saveBatch或 JDBC Batch 操作 - 开启事务需谨慎——可在 listener 外层控制,或改用无事务的异步任务 + 最终一致性
- 前端上传后轮询接口获取进度/结果,避免请求超时
简单监听器示意:
public class UserImportListener extends AnalysisEventListener<userimportdto> {
private static final int BATCH_SIZE = 5000;
private final List<userimportdto> cachedDataList = new ArrayList();
private final UserService userService;
public UserImportListener(UserService userService) {
this.userService = userService;
}
@Override
public void invoke(UserImportDTO data, AnalysisContext context) {
cachedDataList.add(data);
if (cachedDataList.size() >= BATCH_SIZE) {
saveBatch();
cachedDataList.clear();
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (!cachedDataList.isEmpty()) {
saveBatch();
}
}
private void saveBatch() {
userService.saveBatchFromExcel(cachedDataList);
}
}</userimportdto></userimportdto>
不复杂但容易忽略:导入导出接口要加防重复提交(如 Token 机制)、记录操作日志、对敏感字段脱敏处理,生产环境务必压测单次导出 50 万行以上表现。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










