ARTICLE · 1030513
Fesod:下一代高性能 Java Excel 读写库
环境:Spring Boot 3.5.0
1. 简介
Apache Fesod (Incubating) 是一个高性能、内存高效的 Java 库,用于读写电子表格文件,旨在简化开发并确保可靠性。
Apache Fesod (Incubating) 可以为开发者和企业提供极大的自由度和灵活性。我们计划在未来引入更多新功能,以持续提升用户体验和工具可用性。
名称 fesod(发音为 /ˈfɛsɒd/),是 "fast easy spreadsheet and other documents"(快速简单的电子表格和其他文档)的首字母缩写,表达了项目的起源、背景和愿景。
特性
高性能读写:Apache Fesod 专注于性能优化,能够高效处理大规模电子表格数据。与一些传统的电子表格处理库相比,它可以显著减少内存消耗
简单易用:该库提供了简单直观的 API,使开发者能够轻松将其集成到项目中,无论是简单的电子表格操作还是复杂的数据处理
流式操作:Apache Fesod 支持流式读取,最大程度地减少一次性加载大量数据的问题。这一设计在处理数十万甚至数百万行数据时尤其重要。
Apache Fesod 需要 Java 1.8 或更高版本。建议使用最新的 LTS 版本的 Java。我们强烈建议使用最新版本的 Apache Fesod,因为最新版本中的性能优化、错误修复和新功能将提升你的使用体验。
目前,Apache Fesod 使用 POI 作为底层包。如果您的项目已包含 POI 相关组件,您需要手动排除 POI 相关的 jar 文件。
<dependency><groupId>org.apache.fesod</groupId><artifactId>fesod-sheet</artifactId><version>2.0.2-incubating</version></dependency>
2.2 快速入门
读Excel
public class UserDataListener implements ReadListener<User> {@Overridepublic void invoke(User user, AnalysisContext context) {System.out.println("解析数据: %s".formatted(user));}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println("所有数据解析完成!");}}String fileName = "f:/user.xlsx";// 读取电子表格文件FesodSheet.read(fileName, User.class, new UserDataListener()).sheet().doRead();
解析数据: User [id=1, name=张三, age=22, email=zhangsan@qq.com]解析数据: User [id=2, name=李四, age=33, email=lisi@gmail.com]解析数据: User [id=3, name=王五, age=44, email=wangwu@163.com]所有数据解析完成!
// 模型类public class User {@ExcelProperty("编号")private Long id;@ExcelProperty("姓名")private String name ;@ExcelProperty("年龄")private Integer age;@ExcelProperty("邮箱")private String email;@ExcelIgnoreprivate String profile ;}// 写数据private static List<User> data() {List<User> list = new ArrayList<>();for (int i = 4; i <= 10; i++) {User user = new User(i + 0L,"姓名 - " + i, new Random().nextInt(100),i + "@qq.com","profile - " + i) ;list.add(user) ;}return list;}public static void main(String[] args) {String fileName = "f:/user.xlsx";// 创建一个名为 "Template" 的工作表并写入数据FesodSheet.write(fileName, User.class).sheet("Template").doWrite(data());}

public class UploadUserListener extends AnalysisEventListener<User> {private static final Logger log = LoggerFactory.getLogger(UploadUserListener.class) ;private final List<User> list = new ArrayList<>();@Overridepublic void invoke(User user, AnalysisContext context) {list.add(user);// 如果数据量大,那么这里进行批次保存操作}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {log.info("所有数据读取完成!");// 在此处可以进行数据的存储操作,如保存到数据库}}// Controller接口定义@PostMapping("/upload")public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) {if (file.isEmpty()) {return ResponseEntity.badRequest().body("请选择一个文件上传!");}try {FesodSheet.read(file.getInputStream(), User.class, new UploadUserListener()).sheet().doRead();return ResponseEntity.ok("文件上传并处理成功!");} catch (IOException e) {e.printStackTrace() ;return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件处理失败!");}}
@GetMapping("download")public void download(HttpServletResponse response) throws IOException {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("user", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");FesodSheet.write(response.getOutputStream(), User.class).sheet("Sheet").doWrite(data());}
导出大型数据集(如数据库转储、日志分析)时,一次性加载所有行会耗尽内存。Fesod 内部使用 Apache POI 的流式 API(SXSSF),但临时 XML 文件可能会占用大量磁盘空间。启用压缩可以减少磁盘占用,但会略微增加 CPU 消耗。
@Testpublic void largeFileWrite() {String fileName = "largeFile" + System.currentTimeMillis() + ".xlsx";try (ExcelWriter excelWriter = FesodSheet.write(fileName, User.class).registerWriteHandler(new WorkbookWriteHandler() {@Overridepublic void afterWorkbookCreate(WorkbookWriteHandlerContext context) {Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();if (workbook instanceof SXSSFWorkbook) {((SXSSFWorkbook) workbook).setCompressTempFiles(true);}}}).build()) {WriteSheet writeSheet = FesodSheet.writerSheet("模板").build();// 分批写入数据 — 每次 data() 调用返回一个批次for (int i = 0; i < 1000; i++) {excelWriter.write(data(), writeSheet);}}}
数据(内存中,分批处理) Fesod POI/SXSSF│ │ │├─ 100 行批次 ───────────────▶ write() ──────▶ 临时 XML(压缩)├─ 100 行批次 ───────────────▶ write() ──────▶ 临时 XML(追加)│ ...(1000 批次) │ │└─ close() ──────────────────▶ 完成 ─────────▶ 最终 .xlsx
使用 ExcelWriter(try-with-resources)进行批量写入,而非通过 doWrite() 一次性加载全部数据
在磁盘受限的环境中启用临时文件压缩
根据行宽和可用内存调整批次大小(此处为 100 行)
监控临时目录大小:FileUtils.getPoiFilesPath()
千万级数据查询零 OOM:Spring Boot 流式查询终极实战方案
配置即接口:基于 Spring Boot 3 打造零代码动态网关组件
高级开发!实现一套通用 Controller/Service/DAO 效率提升300%
Spring Boot 二进制数据传输:从内存优化到流式处理与断点续传
一行代码搞定!Spring Boot 实现 Word 转 HTML 在线预览
告别Filter!Spring Boot 轻松实现 Request Body 重复读取
动态JSON不要再使用Map!Spring Boot + JsonNode完美解决