原创 Shepherd Shepherd进阶笔记 2024-04-18 08:40 浙江
Shepherd进阶笔记
专注分享Java技术进阶知识点,日常工作点点滴滴,滴水穿石
58篇原创内容
公众号
1.简介
在日常的开发工作中,Excel 文件的读取和写入是非常常见的需求,特别是在后台管理系统中更为频繁,基于传统的方式操作excel通常比较繁琐,EasyExcel 库的出现为我们带来了更简单、更高效的解决方案。本文将介绍 EasyExcel 库的基本用法和一些常见应用场景,帮助开发者更好地利用 EasyExcel 提高工作效率。官网地址:https://github.com/alibaba/easyexcel
青出于蓝而胜于蓝
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便
16M内存23秒读取75M(46W行25列)的Excel(3.2.1+版本)
当然还有极速模式能更快,但是内存占用会在100M多一点 !
EasyExcel 的核心类主要包括以下几个:
-
ExcelReader: 用于读取 Excel 文件的核心类。通过 ExcelReader 类可以读取 Excel 文件中的数据,并进行相应的处理和操作。
-
ExcelWriter: 用于写入 Excel 文件的核心类。通过 ExcelWriter 类可以将数据写入到 Excel 文件中,并进行样式设置、标题添加等操作。
-
AnalysisEventListener: 事件监听器接口,用于处理 Excel 文件读取过程中的事件,如读取到每一行数据时的操作。
-
AnalysisContext: 读取 Excel 文件时的上下文信息,包括当前行号、sheet 名称等。通过 AnalysisContext 可以获取到读取过程中的一些关键信息。
-
WriteHandler: 写入 Excel 文件时的处理器接口,用于处理 Excel 文件的样式设置、标题添加等操作。
-
WriteSheet: 写入 Excel 文件时的 Sheet 配置类,用于指定写入数据的 Sheet 名称、样式等信息。
这些核心类在 EasyExcel 中承担了不同的角色,协作完成了 Excel 文件的读取和写入操作。开发者可以根据具体的需求和场景,使用这些类来实现 Excel 文件的各种操作。
Alibaba EasyExcel的核心入口类是EasyExcel
类,就想我们平时封装的Util类一样,通过它对excel数据读取或者导出。
2.EasyExcel数据导入
2.1.简单导入
准备excel数据文件
这里以用户信息数据为例
定义用户信息User类
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User {
private Long id;
private String userNo;
private String name;
private Integer gender;
private Date birthday;
private String phone;
private String email;
@ExcelIgnore
private Integer isDelete;
private String address;
}
事件监听器ReadListener
我们一般使用EasyExcel
的 AnalysisEventListener
,用于监听 Excel 文件读取事件的接口,通过实现这个接口,可以在读取 Excel 文件的过程中对数据进行处理和操作:
/**
* @author fjzheng
* @version 1.0
* @date 2024/4/12 16:22
*/
@Slf4j
public class UserExcelListener extends AnalysisEventListener<User> {
/**
* 每隔100条处理下,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List<User> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
*
* @param exception
* @param context
* @throws Exception
*/
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
log.error("======>>>解析异常:", exception);
throw exception;
}
/**
* 当读取到一行数据时,会调用这个方法,并将读取到的数据以及上下文信息作为参数传入
* 可以在这个方法中对读取到的数据进行处理和操作,处理数据时要注意异常错误,保证读取数据的稳定性
* @param user
* @param context
*/
@Override
public void invoke(User user, AnalysisContext context) {
log.info("解析到一条数据:{}", user);
cachedDataList.add(user);
if (cachedDataList.size() >= BATCH_COUNT) {
// 处理缓存的数据,比如说入库。。。
// 然后清空
cachedDataList.clear();
}
}
/**
* 当每个sheet所有数据读取完毕后,会调用这个方法,可以在这个方法中进行一些收尾工作,如资源释放、数据汇总等。
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 收尾工作,处理剩下的缓存数据。。。
log.info("sheet={} 所有数据解析完成!", context.readSheetHolder().getSheetName());
}
}
注意 UserExcelListener
不能被Spring管理,要每次读取excel都要new一个新的监听器,如果里面用到spring bean,可以通过构造方法传进去
执行测试用例
/**
* @author fjzheng
* @version 1.0
* @date 2024/4/11 18:04
*/
@SpringBootTest
@RunWith(SpringRunner.class)
@Slf4j
public class ExcelTest {
@Test
public void testExcelRead() {
String fileName = "/Users/shepherdmy/Desktop/test1.xlsx";
EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet().doRead();
// 如果excel是多行表头比如说2行,需要设置行头数headRowNumber,默认不设置为1行表头,sheet不传默认读取第一个sheet
// EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet().headRowNumber(2).doRead();
}
}
执行结果控制台输出如下:
解析到一条数据:User(id=1, userNo=she001, name=王小二, gender=0, birthday=Fri Apr 12 10:00:00 CST 2024, phone=123456789, email=123456789@126.com, isDelete=null, address=杭州市余杭区未来科技城)
解析到一条数据:User(id=2, userNo=she002, name=张三, gender=1, birthday=Wed Apr 10 18:00:00 CST 2024, phone=987654321, email=123456789@qq.com, isDelete=null, address=杭州市拱墅区城西银泰)
解析到一条数据:User(id=3, userNo=she003, name=李四, gender=1, birthday=Thu Apr 11 18:00:00 CST 2024, phone=345686789, email=23445654@163.com, isDelete=null, address=上海陆家嘴浦东)
sheet=用户信息 所有数据解析完成!
完美解析导入,你是否注意到上面我们定义User类的属性
和excel文件的字段一一对应,顺序高度一致,如果顺序映射不一致,比如说定义的时候把姓名name和性别gender的字段顺序调换,会导致导入解析字符串类型转整数失败,这是因为在 EasyExcel 中,当导入数据时,默认情况下是按照 Excel 表格中列的顺序来映射数据的。也就是说,如果 Excel 表格的第一列对应 Java 对象的第一个属性,第二列对应第二个属性,以此类推。
在源码层面,EasyExcel 是通过反射机制来实现数据的映射的。具体来说,EasyExcel 在读取 Excel 文件时,会根据 Java 对象的属性顺序和 Excel 表格中列的顺序来一一对应。这个过程是在 EasyExcel 内部进行的,开发者不需要关心具体的实现细节,只需要提供正确的 Java 对象结构和 Excel 表格格式即可。
项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用
Github地址:https://github.com/plasticene/plasticene-boot-starter-parent
Gitee地址:https://gitee.com/plasticene3/plasticene-boot-starter-parent
微信公众号:Shepherd进阶笔记
交流探讨qun:Shepherd_126
2.2 EasyExcel提供的注解
上面User
类的定义使用了注解@ExcelIgnore
,表示字段isDelete
不参与数据映射,要不然严格按照顺序就会把excel的地址列映射给isDelete
导致字符串转整数类型转换错误了。
EasyExcel 提供了一些注解,用于帮助开发者更灵活地控制 Excel 文件的读取和写入操作。以下是 EasyExcel 中常用的注解:
@ExcelProperty
@ExcelProperty
注解用于标注 Java 对象中的属性与 Excel 表格中的列的对应关系。该注解包含以下属性:
-
value
:指定 Excel 表格中的列索引,表示该属性对应的列在 Excel 表格中的位置。支持字符串形式(如 "A"、"B"、"C")和数字形式(从 0 开始)。 -
index
:同 value,用于指定 Excel 表格中的列索引。 -
index
和value
二选一即可,用于指定 Excel 表格中的列索引。
示例:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "tb_user")
public class User {
@TableId(type = IdType.AUTO)
@ExcelProperty(index = 0)
private Long id;
@ExcelProperty(index = 1)
private String userNo;
@ExcelProperty(index = 3)
// @ExcelProperty("性别")
private Integer gender;
@ExcelProperty(index = 2)
// @ExcelProperty("姓名")
private String name;
@ExcelProperty(index = 4)
private Date birthday;
@ExcelProperty(index = 5)
private String phone;
@ExcelProperty(index = 6)
private String email;
// @ExcelIgnore
private Integer isDelete;
@ExcelProperty(index = 7)
private String address;
}
一开始我们定义User类时并没有使用@ExcelProperty()
,但其实等价于按照类属性顺序从0开始加上了@ExcelProperty(index =列顺序号)
,注意我特意把字段属性姓名name和性别gender调换了顺序,但是index是按照excel来写的,同时我去掉了isDelete的忽略属性@ExcelIgnore
。正常解析导入。。。
在我们日常开发过程中,我个人建议在excel映射的类上必须使用@ExcelProperty()
注解标注,至于使用@ExcelProperty(index = 2)
还是@ExcelProperty("姓名")
都可以,但不能同时用,我个人倾向于@ExcelProperty("姓名")
,这样可以备注字段含义,当然一般人不太会修改excel表头的字段,但是很有可能因为显示查看会拖动excel列的顺序这样就会导致@ExcelProperty(index =列顺序号)
映射出错。
@ExcelIgnore
@ExcelIgnore
注解用于标注 Java 对象中的属性,表示在 Excel 文件的读取和写入过程中忽略该属性。示例如上描述
@ExcelIgnoreUnannotated
@ExcelIgnoreUnannotated
注解用于指定在读取 Excel 文件时是否忽略未标注 @ExcelProperty
注解的属性,默认为 false,即不忽略。上面我们去掉了isDelete的@ExcelIgnore
能正常导出那是因为我们excel文件只有8列刚刚好够标注了@ExcelProperty
映射,加入我们excel文件在添加一列,就会去映射isDelete,不符合我们预期,这时候我们就需要在类使用**@ExcelIgnoreUnannotated**解决此问题
@ExcelSheet
@ExcelSheet
注解用于指定读取 Excel 文件时的 Sheet 名称。通常用于读取多个 Sheet 的情况。
@ExcelIgnoreRowNum
@ExcelIgnoreRowNum
注解用于标注 Java 对象中的属性,表示在 Excel 文件的读取过程中忽略行号。通常用于读取时不需要关注行号的情况。
@ColumnWidth
@ColumnWidth
主要是控制列宽
2.1.3 日期、数字或者自定义格式转换
数据转换我们只需要实现easyexcel封装的Converter
接口即可,比如在上面的用户信息类User
的学号属性userNo统一在excel的值加上前缀uno:
, 性别属性gender由字符串转枚举值男:0 女:1 未知:2
UserNoConverter
public class UserNoConverter implements Converter<String> {
/**
* 支持的java类型
* @return
*/
@Override
public Class<?> supportJavaTypeKey() {
return String.class;
}
/**
* 支持的excel值类型
* @return
*/
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 这里读的时候会调用
*
* @param context
* @return
*/
@Override
public String convertToJavaData(ReadConverterContext<?> context) {
return "uno:" + context.getReadCellData().getStringValue();
}
/**
* 这里是写的时候会调用 不用管
*
* @return
*/
@Override
public WriteCellData<?> convertToExcelData(WriteConverterContext<String> context) {
return new WriteCellData<>(context.getValue());
}
}
GenderConverter
public class GenderConverter implements Converter<Integer> {
@Override
public Class<?> supportJavaTypeKey() {
return Integer.class;
}
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 从excel读数据时候调用
* @param cellData
* @param contentProperty
* @param globalConfiguration
* @return
*/
@Override
public Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
String value = cellData.getStringValue();
if (StringUtils.isBlank(value)){
// 未知
return 2;
}
if (value.indexOf('男') != -1) {
return 0;
}
if (value.indexOf('女') != -1) {
return 1;
}
return 2;
}
/**
* 写数据到excel里面
* @param context
* @return
*/
@Override
public WriteCellData<?> convertToExcelData(WriteConverterContext<Integer> context) {
Integer value = context.getValue();
if (Objects.equals(value, 0)) {
return new WriteCellData<>("男");
}
if (Objects.equals(value, 1)) {
return new WriteCellData<>("女");
}
return new WriteCellData<>("未知");
}
}
在映射的实体类User
进行转换器绑定:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
// @ExcelIgnoreUnannotated
public class User {
@ExcelProperty(index = 0)
private Long id;
@ExcelProperty(index = 1, converter = UserNoConverter.class)
private String userNo;
@ExcelProperty(index = 3, converter = GenderConverter.class)
// @ExcelProperty("性别")
private Integer gender;
@ExcelProperty(index = 2)
// @ExcelProperty("姓名")
private String name;
@ExcelProperty(index = 4)
private Date birthday;
@ExcelProperty(index = 5)
private String phone;
@ExcelProperty(index = 6)
private String email;
@ExcelIgnore
private Integer isDelete;
@ExcelProperty(index = 7)
private String address;
}
执行结果:
2.1.4 读取多个sheet
我们前面用户信息基础再加了一个sheet2接着存用户信息,同时新增一个sheet=银行信息存储银行账户信息:
银行账号信息类:
@Data
public class Account {
private String name;
private String idCard;
private String cardNo;
private BigDecimal amount;
private Integer status;
private Date lastUsedTime;
}
如果我们没有银行信息这个sheet,我们可以用如下代码读写所有sheet:因为每个sheet的数据格式是一样的
// 读取所有sheet
EasyExcel.read(fileName, User.class, new UserExcelListener()).doReadAll();
每个sheet的数据格式不一样,只能单独一一处理,新建一个处理银行账号信息的监听器:
@Slf4j
public class AccountExcelListener extends AnalysisEventListener<Account> {
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
log.error("======>>>解析异常:", exception);
}
@Override
public void invoke(Account data, AnalysisContext context) {
log.info("解析到一条数据:{}", data);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
log.info("sheet={} 所有数据解析完成!", context.readSheetHolder().getSheetName());
}
/**
* 解析表头数据
* @param headMap
* @param context
*/
@Override
public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
log.info("表头数据:{}", ConverterUtils.convertToStringMap(headMap, context));
}
}
测试用例方法改为如下:
@Test
public void testExcelRead() {
String fileName = "/Users/shepherdmy/Desktop/testExcel.xlsx";
// 读取用户信息两个sheet
EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet(0).doRead();
EasyExcel.read(fileName, User.class, new UserExcelListener()).sheet(1).doRead();
// 读取银行账户信息
EasyExcel.read(fileName, Account.class, new AccountExcelListener()).sheet(2).doRead();
}
执行结果:
解析到一条数据:User(id=1, userNo=uno:she001, gender=2, name=王小二, birthday=Fri Apr 12 10:00:00 CST 2024, phone=123456789, email=123456789@126.com, isDelete=null, address=杭州市余杭区未来科技城)
解析到一条数据:User(id=2, userNo=uno:she002, gender=2, name=张三, birthday=Wed Apr 10 18:00:00 CST 2024, phone=987654321, email=123456789@qq.com, isDelete=null, address=杭州市拱墅区城西银泰)
解析到一条数据:User(id=3, userNo=uno:she003, gender=2, name=李四, birthday=Thu Apr 11 18:00:00 CST 2024, phone=345686789, email=23445654@163.com, isDelete=null, address=上海陆家嘴浦东)
sheet=用户信息 所有数据解析完成!
解析到一条数据:User(id=5, userNo=uno:she005, gender=2, name=小五, birthday=Fri Apr 12 10:00:00 CST 2024, phone=123456789, email=123456789@126.com, isDelete=null, address=成都太古里)
解析到一条数据:User(id=6, userNo=uno:she006, gender=2, name=小六, birthday=Wed Apr 10 18:00:00 CST 2024, phone=987654321, email=123456789@qq.com, isDelete=null, address=上海南京路)
解析到一条数据:User(id=7, userNo=uno:she007, gender=2, name=小七, birthday=Thu Apr 11 18:00:00 CST 2024, phone=345686789, email=23445654@163.com, isDelete=null, address=杭州湖滨路)
sheet=用户信息2 所有数据解析完成!
表头数据:{0=姓名, 1=身份证号, 2=银行卡号, 3=余额, 4=状态, 5=最近使用时间}
解析到一条数据:Account(name=张三, idCard=3322114566, cardNo=62023445556, amount=10000.88, status=0, lastUsedTime=Sun Dec 31 00:00:00 CST 2023)
解析到一条数据:Account(name=李四, idCard=53134557, cardNo=623454576878, amount=6666.66, status=1, lastUsedTime=Sun Jun 11 00:00:00 CST 2023)
sheet=银行信息 所有数据解析完成!
3.EasyExcel数据导出
使用EasyExcel数据导出相对来说很简单,直接上代码,先来看看用户信息类User
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
@ExcelProperty(index = 0)
private Long id;
@ExcelProperty(index = 1, converter = UserNoConverter.class)
private String userNo;
@ExcelProperty(value = "性别", converter = GenderConverter.class)
private Integer gender;
@ExcelProperty(value = "姓名")
private String name;
@ExcelProperty(index = 4)
private Date birthday;
@ExcelProperty(index = 5)
private String phone;
@ExcelProperty(index = 6)
private String email;
@ExcelIgnore
private Integer isDelete;
@ExcelProperty(index = 7)
private String address;
}
我们前面说过使用@ExcelProperty
时,不建议同时使用index和value去映射属性,我这里这么写只是让你看看导出效果来突出使用value@ExcelProperty(value = "姓名")
的好处,话不多说直接看测试用例,我造了10条用户信息数据导出:
@Test
public void testExcelWrite() {
List<User> userList = new ArrayList<>();
int i = 0;
while (i < 10) {
User user = User.builder().id((long)i).userNo("no-" + i).birthday(new Date()).gender(i%3)
.phone("123456789"+i).email("she12dfe@163.com").name("芽儿哟"+i).address("杭州"+i).build();
userList.add(user);
i++;
}
String fileName = "export.xlsx";
EasyExcel.write(fileName, User.class).sheet("员工信息").doWrite(userList);
}
导出文件如下所示:
可以看到表头默认为属性名,使用@ExcelProperty(value = "姓名")
就使用value更符合我们日常需求习惯。
假如导出大批量数据,我们不能一次性查出上百万条数据放入内存一次性导出,很有可能导致OOM,只能分页查询分批导出,实现如下:还是以上面的10天用户信息数据模拟,每3条处理一次导出
@Test
public void testExcelWrite() {
List<User> userList = new ArrayList<>();
int i = 0;
while (i < 10) {
User user = User.builder().id((long)i).userNo("no-" + i).birthday(new Date()).gender(i%3)
.phone("123456789"+i).email("she12dfe@163.com").name("芽儿哟"+i).address("杭州"+i).build();
userList.add(user);
i++;
}
String fileName = "export.xlsx";
// 导出大批量数据,我们不能一次查询所有数据到内存中,只能分页查询导出,这里UserList假设需要分页导出,每页查询3条
int size = userList.size();
int page = size/ 3 + (size % 3 == 0 ? 0 : 1);
// // 创建excelWriter
// ExcelWriter excelWriter = EasyExcel.write(fileName, User.class).build();
// // 创建writeSheet
// WriteSheet writeSheet = EasyExcel.writerSheet(0,"分页员工信息").build();
// for (int k = 0; k < page; k++) {
// int start = k * 3;
// int end = (k + 1) * 3 >= size ? size : (k + 1) * 3;
// // 这里其实应该是分页查询数据,下面只是简单模拟下
// List<User> users = userList.subList(start, end);
// excelWriter.write(users, writeSheet);
// }
// // 关闭io才会写入数据,必须执行,所以最好用try-with-source安全,如下
// excelWriter.finish();
// 官方安全写法
try (ExcelWriter excelWriter = EasyExcel.write(fileName, User.class).build()) {
// 这里注意 如果同一个sheet只要创建一次
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// 去调用写入,实际使用时根据数据库分页的总的页数来
for (int k = 0; k < page; k++) {
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
int start = k * 3;
int end = (k + 1) * 3 >= size ? size : (k + 1) * 3;
List<User> users = userList.subList(start, end);
excelWriter.write(users, writeSheet);
}
}
}
动态表头和动态数据
上面的示例都是映射Java类的,其实我可以直接导出excel,表头数据无外乎就是第一行,我们可以灵活设置即可:
@Test
public void testExport() {
List<String> heads = ListUtils.newArrayList("员工编号", "姓名", "性别", "手机号", "金额");
// 表头
List<List<String>> dataList = new ArrayList<>();
dataList.add(heads);
int i = 0;
while (i < 10) {
i++;
dataList.add(ListUtils.newArrayList(String.valueOf(i), "王"+i, i%2 == 0 ? "男" : "女", "1234456", "666.88"));
}
String fileName = "export.xlsx";
EasyExcel.write(fileName).sheet("员工").doWrite(dataList);
}
更多导出实现比如说设置单元格样式颜色啥的,可查看官方文档:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write
4.EasyExcel与poi对比
EasyExcel 和 Apache POI 是两个常用的 Java 库,用于处理 Excel 文件。它们有着一些相似之处,同时也有一些不同之处。以下是它们之间的对比:
易用性
-
EasyExcel:EasyExcel 提供了简单易用的 API,可以方便地进行 Excel 文件的读写操作。它使用了注解来简化开发者的操作,支持直接将 Java 对象写入 Excel 文件,也支持将 Excel 文件转换为 Java 对象。
-
Apache POI:Apache POI 是一个功能强大但相对复杂的库,需要较多的代码来完成 Excel 文件的读写操作。使用 POI 进行 Excel 操作通常需要处理大量的低级 API 调用和数据转换。
性能
-
EasyExcel:EasyExcel 在性能上表现优异,通过内存映射等技术来优化文件的读写操作,可以处理大型 Excel 文件而不会出现内存溢出等问题。
-
Apache POI:Apache POI 在处理大型 Excel 文件时可能会出现性能问题,特别是在写入大量数据时,可能会占用大量的内存和 CPU 资源。
功能丰富程度
-
EasyExcel:EasyExcel 提供了丰富的功能,支持多种样式、合并单元格、图片插入等功能,同时还提供了注解和模板的方式来简化开发者的操作。
-
Apache POI:Apache POI 是一个功能强大的库,提供了丰富的 API 来操作 Excel 文件,支持多种格式的 Excel 文件读写,并且可以进行复杂的操作,如图表、公式、宏等。
社区和文档支持
-
EasyExcel:EasyExcel 是阿里巴巴开源的项目,拥有活跃的社区和丰富的文档支持。开发者可以在社区中获取到丰富的使用经验和问题解决方案。
-
Apache POI:Apache POI 是 Apache 软件基金会的项目,拥有庞大的社区和完善的文档支持。开发者可以在官方网站上找到详尽的文档和示例代码。
总的来说,EasyExcel 相对于 Apache POI 更易于使用且性能更好,特别适合处理大型 Excel 文件和简单的数据导出导入需求。而 Apache POI 则更加灵活,支持更多复杂的 Excel 操作,适用于对功能要求较高的场景。选择使用哪个库取决于具体的需求和开发者的偏好
5.总结
EasyExcel 是一个功能强大、易于使用的 Excel 操作工具,它大大简化了 Excel 数据的读取和写入操作,提高了开发效率。通过本文的介绍,相信读者对 EasyExcel 库有了更深入的了解,希望能够在实际开发中更多地利用 EasyExcel 提升工作效率。