目录
[二、FastExcel 介绍](#二、FastExcel 介绍)
[2.1 FastExcel 是什么](#2.1 FastExcel 是什么)
[2.2 FastExcel 主要特点](#2.2 FastExcel 主要特点)
[2.3 FastExcel 主要功能](#2.3 FastExcel 主要功能)
[2.4 FastExcel 核心技术实现原理](#2.4 FastExcel 核心技术实现原理)
[三、springboot 整合FastExcel 操作过程](#三、springboot 整合FastExcel 操作过程)
[3.1 开发环境准备](#3.1 开发环境准备)
[3.2 完整整合过程](#3.2 完整整合过程)
[3.2.1 添加依赖](#3.2.1 添加依赖)
[3.2.2 配置文件信息](#3.2.2 配置文件信息)
[3.2.3 增加一个实体类](#3.2.3 增加一个实体类)
[3.2.4 增加一个事件监听器](#3.2.4 增加一个事件监听器)
[3.2.5 增加一个测试使用的接口类](#3.2.5 增加一个测试使用的接口类)
[3.2.6 效果测试与验证](#3.2.6 效果测试与验证)
[3.2.7 补充说明](#3.2.7 补充说明)
[3.3 FastExcel 高级功能](#3.3 FastExcel 高级功能)
[3.3.1 大量数据导入优化](#3.3.1 大量数据导入优化)
[3.3.2 excel转pdf功能](#3.3.2 excel转pdf功能)
[3.4 EasyExcel 升级到FastExcel](#3.4 EasyExcel 升级到FastExcel)
[3.4.1 修改依赖](#3.4.1 修改依赖)
[3.4.2 修改代码](#3.4.2 修改代码)
一、前言
在微服务项目中经常会涉及到excel的导入导出功能,随着要处理的业务数据量越来越大,这也对程序提出了更高的要求,因为大批量的数据导入导出对性能提出了更高的挑战。在早些年使用poi等工具进行导入导出的时候,经常会被吐槽有各种问题,比如API使用复杂,数据量过大时容易OOM等问题,所以在后面当EasyExcel推出来之后,其优异的性能得到了很多程序开发者的认可。可惜的是,随着EasyExcel的设计者离场,EasyExcel的维护面临着考验,于是又推出FastExcel 。作为EasyExcel的替代者,不仅完全兼容EasyExcel的功能,同时也对其进行了进一步的优化,并增加了一些新的功能,本篇将详细介绍下FastExcel 的使用。
二、FastExcel 介绍
2.1 FastExcel 是什么
FastExcel 是一款基于 Java 的开源库,旨在提供快速、简洁且能解决大文件内存溢出问题的 Excel 处理工具。它兼容 EasyExcel,提供性能优化、bug 修复,并新增了如读取指定行数和将 Excel 转换为 PDF 的功能。
FastExcel 以 MIT 协议发布,适用于任何商业场景。其高性能读写、简单易用的 API 和流式操作能力,使其特别适合处理大规模数据。FastExcel 支持无缝从 EasyExcel 迁移,极大地简化了 Excel 文件的读写操作,提升了开发效率。git地址:GitHub - apache/fesod: Fast. Easy. Done. Processing Excels without worrying about large files causing OOM.

2.2 FastExcel 主要特点
FastExcel 具备如下特点:
-
完全兼容EasyExcel的所有功能和特性,这使得用户可以无缝迁移和过渡;
-
从EasyExcel升级到EasyExcel,只需要简单的更换包名和maven依赖即可完成成升级;
-
从功能上,比EasyExcel提供更多创新改进,比如 FastExcel 1.0.0版本新增了:
-
读取excel指定的行数;
-
将excel转为pdf;
-
2.3 FastExcel 主要功能
FastExcel 主要提供了以下功能
-
高性能读写:FastExcel 专注于性能优化,能高效处理大规模 Excel 数据,显著降低内存占用。
-
简单易用:提供简洁直观的 API,易于集成和使用。
-
流式操作:支持流式读取,减少一次性加载大量数据的问题。
-
读取指定行数:可以根据需求,只读取感兴趣的部分数据,提高数据处理效率。
-
Excel 转换为 PDF:支持直接将 Excel 文件转换为 PDF,满足多样化的文档输出需求。
2.4 FastExcel 核心技术实现原理
FastExcel 核心原理参考下面的理解:
-
内存优化:基于流式读取技术,不需要一次性将整个 Excel 文件加载到内存中,逐行或逐块读取数据。
-
事件驱动模型 :基于实现
ReadListener
接口处理读取操作。当读取到数据时,会触发接口中的方法,如invoke
方法,支持开发者对每行数据进行即时处理。 -
注解映射:用注解将 Excel 文件中的列与 Java 对象的属性进行映射。开发者能轻松地将 Excel 数据转换为 Java 对象,同时也支持反向操作,将 Java 对象写入 Excel。
三、springboot 整合FastExcel 操作过程
接下来通过实际案例演示下如何在springboot 项目中整合FastExcel 。
3.1 开发环境准备
为了确保后续你的工程也能正常的使用,建议参考下面的技术栈:
-
JDK 17;
-
SpringBoot 3.3 ;
3.2 完整整合过程
3.2.1 添加依赖
创建一个全新的springboot工程,在pom文件中添加如下的依赖
java
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
3.2.2 配置文件信息
本例仅演示导入导出功能,配置文件只需要配置下端口即可
java
server:
port: 8082
3.2.3 增加一个实体类
之前有使用过easyexcel经验的同学应该不陌生,一般导入的数据最终需要落库,为了让excel的列与数据库表字段进行对应,一般需要一个自定义的对象,用easyexcel提供的相应注解来标准字段信息,这样easyexcel在解析的时候才能进行映射,如下,增加一个自定义的User类,里面有3个基本属性
java
package com.congge.entity;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.*;
@Setter
@Getter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {
@ExcelProperty("编号")
private Integer id;
@ExcelProperty("名字")
private String name;
@ExcelProperty("年龄")
private Integer age;
}
3.2.4 增加一个事件监听器
监听器的作用也可以理解为FastExcel 内部封装的一个兼具数据读取,数据缓存,数据转换为一体的组件,有了这个监听器,上层只需要在上传或下载excel的时候代入进去,该监听器就可以发挥作用,从而完成数据的转换。参考下面的代码。
java
package com.congge.component;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
public class BaseExcelListener<T> extends AnalysisEventListener<T> {
private final List<T> dataList = new ArrayList<>();
@Override
public void invoke(T t, AnalysisContext analysisContext) {
// 每读取一行数据,就将其添加到dataList中
dataList.add(t);
System.out.println("解析到数据: " + t);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("读取完成,共读取了 " + dataList.size() + " 条数据");
}
public List<T> getDataList() {
return dataList;
}
}
3.2.5 增加一个测试使用的接口类
为了方便测试看效果,增加一个controller控制器类,提供2个基本的上传下载接口,参考下面的代码
java
package com.congge.controller;
import cn.idev.excel.FastExcel;
import com.congge.component.BaseExcelListener;
import com.congge.entity.User;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
@RestController
@RequestMapping("/api/excel")
public class ExcelController {
/**
* Excel导出功能
* @param response
* @throws IOException
* localhost:8082/api/excel/download
*/
@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("用户数据", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 写入数据
FastExcel.write(response.getOutputStream(), User.class)
.sheet("用户信息")
.doWrite(buildData());
}
/**
* Excel导出功能
* @param file
* @return
* localhost:8082/api/excel/upload
*/
@PostMapping("/upload")
public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("请选择一个文件上传!");
}
try {
BaseExcelListener<User> baseExcelListener = new BaseExcelListener<>();
FastExcel.read(file.getInputStream(), User.class, baseExcelListener)
.sheet()
.doRead();
// 获取读取的数据
List<User> dataList = baseExcelListener.getDataList();
System.out.println("读取到的数据: " + dataList);
return ResponseEntity.ok("文件上传并处理成功!共处理 " + dataList.size() + " 条数据");
} catch (IOException e) {
return ResponseEntity.status(500).body("文件处理失败: " + e.getMessage());
}
}
// 创建测试数据
private List<User> buildData() {
User user1 = new User();
user1.setId(1);
user1.setName("张三");
user1.setAge(18);
User user2 = new User();
user2.setId(2);
user2.setName("李四");
user2.setAge(19);
return List.of(user1, user2);
}
}
3.2.6 效果测试与验证
分别测试一下上面两个接口,启动工程
1)下载接口测试
启动工程后,浏览器调用接口:localhost:8082/api/excel/download *,*按照预期的效果下载为一个excel

2)上传接口测试
使用接口工具模拟接口调用,就用上面的这个excel,主要是看看控制台的输出日志,接口调用成功

通过控制台的输出日志可以看到,待导入的数据也成功进行了加载

3.2.7 补充说明
有的同学可能会有这样的担心,当excel数据量太大的时候,会不会有性能问题,关于这一点,FastExcel 的作者在设计的时候就考虑到了,当你要上传的excel里面的数据量比较大,工具方法内部会进行分批读取,从而减少OOM的风险

3.3 FastExcel 高级功能
3.3.1 大量数据导入优化
对于大数据量导入,建议使用批量处理模式,因此需要改写和优化上述的事件监听器,参考下面的代码
java
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
public class BatchExcelListener<T> extends AnalysisEventListener<T> {
private static final int BATCH_COUNT = 1000;
private List<T> dataList = new ArrayList<>(BATCH_COUNT);
@Override
public void invoke(T data, AnalysisContext context) {
dataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (dataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
dataList = new ArrayList<>(BATCH_COUNT);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 确保最后遗留的数据也存储到数据库
if (!dataList.isEmpty()) {
saveData();
}
System.out.println("所有数据解析完成!");
}
private void saveData() {
// 这里实现批量保存到数据库的逻辑
System.out.println("批量保存" + dataList.size() + "条数据到数据库");
// userService.saveBatch(dataList);
}
}
3.3.2 excel转pdf功能
添加下面的测试接口验证excel转pdf功能
java
//localhost:8082/api/excel/convertToPdf
@GetMapping("/convertToPdf")
public void convertToPdf(){
String excelPath = "D:\\data\\用户数据.xlsx";
String pdfFilePath = "D:\\data\\用户数据.pdf";
FastExcel.convertToPdf(new File(excelPath), new File(pdfFilePath), null, null);
System.out.println("转换完成");
}
3.4 EasyExcel 升级到FastExcel
有一些伙伴之前项目中使用的是EasyExcel ,那么如果要升级到FastExcel该怎么做呢,可以参考下面的步骤
3.4.1 修改依赖
将 EasyExcel 的依赖替换为 FastExcel 的依赖,如下:
java
<!-- easyexcel 依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>xxxx</version>
</dependency>
依赖替换为:
java
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.0.0</version>
</dependency>
3.4.2 修改代码
将 EasyExcel 的包名替换为 FastExcel 的包名,如下:
java
// 将 easyexcel 的包名替换为 FastExcel 的包名
import com.alibaba.excel.**;
替换为:
java
import cn.idev.excel.**;
四、写在文末
本文通过案例代码演示了FastExcel 的使用,通过案例介绍了FastExcel 具体的导入导出的使用,更多功能有兴趣的同学可以继续深入研究,本篇到此结束,感谢观看。