【微服务】SpringBoot 整合 FastExcel 实现导入导出操作详解
目录
一、前言
二、FastExcel 介绍
2.1 FastExcel 是什么
2.2 FastExcel 主要特点
2.3 FastExcel 主要功能
2.4 FastExcel 核心技术实现原理
三、springboot 整合FastExcel 操作过程
3.1 开发环境准备
3.2 完整整合过程
3.2.1 添加依赖
3.2.2 配置文件信息
3.2.3 增加一个实体类
3.2.4 增加一个事件监听器
3.2.5 增加一个测试使用的接口类
3.2.6 效果测试与验证
3.2.7 补充说明
3.3 FastExcel 高级功能
3.3.1 大量数据导入优化
3.3.2 excel转pdf功能
3.4 EasyExcel 升级到FastExcel
3.4.1 修改依赖
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文件中添加如下的依赖
<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 配置文件信息
本例仅演示导入导出功能,配置文件只需要配置下端口即可
server:port: 8082
3.2.3 增加一个实体类
之前有使用过easyexcel经验的同学应该不陌生,一般导入的数据最终需要落库,为了让excel的列与数据库表字段进行对应,一般需要一个自定义的对象,用easyexcel提供的相应注解来标准字段信息,这样easyexcel在解析的时候才能进行映射,如下,增加一个自定义的User类,里面有3个基本属性
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的时候代入进去,该监听器就可以发挥作用,从而完成数据的转换。参考下面的代码。
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<>();@Overridepublic void invoke(T t, AnalysisContext analysisContext) {// 每读取一行数据,就将其添加到dataList中dataList.add(t);System.out.println("解析到数据: " + t);}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("读取完成,共读取了 " + dataList.size() + " 条数据");}public List<T> getDataList() {return dataList;}
}
3.2.5 增加一个测试使用的接口类
为了方便测试看效果,增加一个controller控制器类,提供2个基本的上传下载接口,参考下面的代码
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 大量数据导入优化
对于大数据量导入,建议使用批量处理模式,因此需要改写和优化上述的事件监听器,参考下面的代码
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);@Overridepublic void invoke(T data, AnalysisContext context) {dataList.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (dataList.size() >= BATCH_COUNT) {saveData();// 存储完成清理 listdataList = new ArrayList<>(BATCH_COUNT);}}@Overridepublic 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功能
//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 的依赖,如下:
<!-- easyexcel 依赖 -->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>xxxx</version>
</dependency>
依赖替换为:
<dependency><groupId>cn.idev.excel</groupId><artifactId>fastexcel</artifactId><version>1.0.0</version>
</dependency>
3.4.2 修改代码
将 EasyExcel 的包名替换为 FastExcel 的包名,如下:
// 将 easyexcel 的包名替换为 FastExcel 的包名
import com.alibaba.excel.**;
替换为:
import cn.idev.excel.**;
四、写在文末
本文通过案例代码演示了FastExcel 的使用,通过案例介绍了FastExcel 具体的导入导出的使用,更多功能有兴趣的同学可以继续深入研究,本篇到此结束,感谢观看。