springboot excel 表格入门与实战
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,也不读取@ExcelIgnoreprivate 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);// 逐行回调:每读取一行数据,触发该方法@Overridepublic 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(); // 清空列表,释放内存}}// 读取完成后回调:所有数据读取完毕后触发@Overridepublic 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
)。