Spring Boot3 FastExcel 项目地址
https://gitee.com/supervol/loong-springboot-study
(记得给个start,感谢)
FastExcel 介绍
FastExcel 是一款轻量、高性能的 Java Excel 处理工具,核心目标是解决传统库(如 Apache POI)在大数据量场景下的内存溢出问题,同时提供简洁易用的 API。其官方核心定位如下:
- 开源许可:采用 Apache License 2.0,允许商业场景免费使用,无版权限制。
- 跨 JDK 支持:兼容 JDK 8 ~ JDK 21(覆盖 Spring Boot 3 所需的 JDK 17+,同时支持旧项目)。
- 底层依赖:基于 Apache POI 封装,但已优化内存模型;若项目已引入 POI,需手动排除冲突依赖。
- 模块合并:2025 年 7 月后已将
fastexcel-core
合并入核心模块fastexcel
FastExcel 特性
功能分类 | 核心能力 |
---|---|
高性能读写 | 优化内存模型,相比传统 POI 减少 60%+ 内存占用,支持百万级数据无 OOM。 |
流式处理 | 原生支持流式读取 / 写入,无需手动处理内存缓冲,数据逐行处理,降低内存峰值。 |
简洁 API | 基于静态工厂类 FastExcel 封装,3 行代码即可实现 Excel 读写,无需关注底层 Sheet/Row/Cell 层级。 |
注解驱动 | 通过 @ExcelProperty 映射列与实体类,@ExcelIgnore 忽略无需导出的字段,配置零侵入。 |
扩展性 | 提供 ReadListener 监听读取过程(如数据校验、批量入库),支持自定义转换器。 |
多格式支持 | 兼容 .xlsx(Office 2007+)、.xls(兼容模式),无需额外配置格式类型。 |
FastExcel 示例
请参考项目地址中 springboot-office/springboot-excel 模块代码。
FastExcel 实战
FastExcel 官方核心 API 围绕 FastExcel
静态类展开,读取需实现 ReadListener
,写入通过注解映射实体类,无复杂 Builder 嵌套。
1. 定义 Excel 映射实体类
使用官方注解 @ExcelProperty
指定列名,@ExcelIgnore
忽略无需导出的字段:
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.ExcelIgnore;
import java.util.Date;
// 官方示例实体类:用户数据
public class UserData {
// @ExcelProperty:指定Excel列名,与表头完全匹配
@ExcelProperty("用户ID")
private Long userId;
@ExcelProperty("用户名")
private String username;
// 日期类型无需额外配置格式(默认 yyyy-MM-dd HH:mm:ss,支持自动转换)
@ExcelProperty("注册时间")
private Date registerTime;
// @ExcelIgnore:该字段不写入Excel,也不读取
@ExcelIgnore
private String password;
// 省略 getter/setter(必须提供,否则无法映射)
}
2. 读取 Excel 文件
通过 实现 ReadListener
接口 处理读取逻辑(支持数据逐行回调,避免一次性加载内存),适用于本地文件或 HTTP 上传文件。
(1) 实现 ReadListener
import cn.idev.excel.listener.ReadListener;
import cn.idev.excel.context.AnalysisContext;
import java.util.ArrayList;
import java.util.List;
// 自定义读取监听器:处理Excel每行数据
public class UserDataListener implements ReadListener<UserData> {
// 批量存储读取到的数据(可根据业务设置批量大小,如1000条入库一次)
private List<UserData> dataList = new ArrayList<>(1000);
// 逐行回调:每读取一行数据,触发该方法
@Override
public void invoke(UserData userData, AnalysisContext context) {
// 1. 数据校验(示例:用户名不能为空)
if (userData.getUsername() == null || userData.getUsername().trim().isEmpty()) {
throw new RuntimeException("第" + context.getRowNum() + "行:用户名不能为空");
}
// 2. 加入临时列表
dataList.add(userData);
// 3. 批量处理(示例:满1000条入库,避免列表过大)
if (dataList.size() >= 1000) {
saveBatchData(); // 调用业务方法批量入库
dataList.clear(); // 清空列表,释放内存
}
}
// 读取完成后回调:所有数据读取完毕后触发
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理剩余数据(不足1000条的部分)
if (!dataList.isEmpty()) {
saveBatchData();
}
System.out.println("Excel读取完成,共处理" + context.getTotalRowNum() + "行数据");
}
// 业务方法:批量保存数据(示例,需结合DAO层实现)
private void saveBatchData() {
// userMapper.batchInsert(dataList); // MyBatis批量入库示例
System.out.println("批量保存" + dataList.size() + "条用户数据");
}
}
(2) 发起读取
场景 A:读取本地 Excel 文件
import cn.idev.excel.FastExcel;
import org.springframework.stereotype.Service;
@Service
public class ExcelImportService {
// 读取本地Excel文件(路径示例:D:/data/user_import.xlsx)
public void importLocalExcel(String filePath) {
// 官方读取API:FastExcel.read(文件路径, 实体类, 监听器).sheet().doRead()
FastExcel.read(filePath, UserData.class, new UserDataListener())
.sheet() // 读取第一个工作表(默认索引0,可指定名称:.sheet("用户导入表"))
.doRead(); // 执行读取
}
}
场景 B:读取 HTTP 上传文件
import cn.idev.excel.FastExcel;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@RestController
public class ExcelImportController {
// 接收前端上传的Excel文件
@PostMapping("/api/excel/import/user")
public String importUser(@RequestParam("file") MultipartFile file) throws IOException {
// 1. 校验文件格式
String fileName = file.getOriginalFilename();
if (fileName == null || (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls"))) {
return "错误:仅支持 .xlsx 或 .xls 格式";
}
// 2. 读取上传文件流(无需手动关闭流,FastExcel内部自动处理)
FastExcel.read(file.getInputStream(), UserData.class, new UserDataListener())
.sheet()
.doRead();
return "导入成功,文件大小:" + file.getSize() + "字节";
}
}
3. 写入 Excel 文件
写入 API 极简,通过 FastExcel.write()
直接生成文件,支持流式写入大数据量(无需手动开启,内部自动判断)。Spring Boot 响应式下载示例代码。
import cn.idev.excel.FastExcel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
@RestController
public class ExcelExportController {
private final UserService userService; // 业务层:查询用户数据
// 构造注入(Spring Boot 3 推荐)
public ExcelExportController(UserService userService) {
this.userService = userService;
}
// 导出用户数据为Excel,浏览器直接下载
@GetMapping("/api/excel/export/user")
public void exportUser(HttpServletResponse response) throws IOException {
// 1. 准备导出数据(模拟大数据量,如10万条)
List<UserData> userList = userService.listAllUsers(); // 业务查询(建议分页查询避免内存占用)
// 2. 设置响应头(指定下载格式、文件名)
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
// 文件名编码:避免中文乱码
String fileName = URLEncoder.encode("用户数据导出_2025.xlsx", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
// 3. 官方写入API:流式写入响应流
FastExcel.write(response.getOutputStream(), UserData.class)
.sheet("用户列表") // 指定工作表名称
.doWrite(userList); // 执行写入(内部自动流式处理,无OOM风险)
}
}
4. 自定义读取 / 写入配置
支持自定义工作表索引、表头行数、数据转换器等,通过链式调用扩展配置:
(1) 自定义读取配置
// 读取第2个工作表(索引1),跳过前2行(表头在第3行)
FastExcel.read(filePath, UserData.class, new UserDataListener())
.sheet(1) // 工作表索引(0开始),也可指定名称:.sheet("2025年用户")
.headRowNumber(2) // 表头行数(默认1行,即第1行为表头,此处表示前2行为说明,第3行为表头)
.doRead();
(2) 自定义日期格式
import cn.idev.excel.converter.LocalDateTimeConverter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// 示例:自定义LocalDateTime格式为 "yyyy年MM月dd日 HH:mm"
public class CustomDateTimeConverter extends LocalDateTimeConverter {
public CustomDateTimeConverter() {
super(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm"));
}
}
// 实体类中使用自定义转换器
public class UserData {
@ExcelProperty(value = "最后登录时间", converter = CustomDateTimeConverter.class)
private LocalDateTime lastLoginTime;
// 其他字段...
}
FastExcel 注意
- POI 依赖冲突:FastExcel 内部依赖 POI 4.4.0+,若项目已引入旧版 POI(如 3.x),需手动排除冲突,否则会出现
NoSuchMethodError
。 - 实体类要求:映射实体类必须提供 无参构造器 和 getter/setter(FastExcel 通过反射获取字段值,缺少会导致映射失败)。
- 大数据量建议:导出超过 100 万条数据时,建议结合 分页查询(如 MyBatis 分页),分批次调用
doWrite()
(示例:每次写入 10 万条,避免一次性查询全量数据)。 - 文件权限:读取本地文件时,确保应用有文件读取权限;写入临时文件时,避免使用系统敏感路径(如
/root
)。