springboot项目整合EasyExcel解决方案
创始人
2025-05-31 21:42:13
0

springboot项目整合EasyExcel解决方案

1、背景

目前项目中采用的是POI 的方式实现导入导出。下面这段话是踩自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模式,在上层做了模型转换的封装,让使用者更加简单方便

2、整合EasyExcel

2.1 引入依赖

com.alibabaeasyexcel3.1.1

2.2 实现导入导出操作:

  • 导入、导出对象实体类:
/*** @author itender* @date 2023/1/30 17:30* @desc*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
// 类上加注解 @ExcelIgnoreUnannotated,过滤属性没有@ExcelProperty注解的字段
@ExcelIgnoreUnannotated
public class User {/*** user id.*/@ExcelProperty("ID")private Long id;/*** 姓名.*/@ExcelProperty("姓名")private String userName;/*** 性别.*/@ExcelProperty("性别")private String gender;/*** 地址.*/@ExcelProperty("地址")private String address;/*** 邮箱.*/@ExcelProperty("邮箱")private String email;/*** 手机号码.*/@ExcelProperty("手机号码")private Long phoneNumber;/*** 描述.*/@ExcelIgnore@ExcelProperty("描述")private String description;
}
  • controller
@GetMapping("/export")
public void exportUserInfo(HttpServletResponse response) {try {response.reset();response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding(StandardCharsets.UTF_8.name());String fileName = "导出用户信息列表";// 注意:这里要加上filename*=utf-8'zh_cn'否则可能会导致导出文件名乱码response.setHeader("Content-disposition","attachment;filename*=utf-8'zh_cn'" + fileName + System.currentTimeMillis() + ".xlsx");userService.exportUserInfo(response.getOutputStream());} catch (IOException e) {e.printStackTrace();}
}@PostMapping("/import")
public void importUserInfo(@RequestParam(value = "file") MultipartFile file) {try {userService.importUserInfo(file.getInputStream());} catch (IOException e) {e.printStackTrace();}
}
  • service
/*** @author itender* @date 2023/1/30 17:43* @desc*/
public interface UserService {/*** 导出文件** @param outputStream*/void exportUserInfo(ServletOutputStream outputStream);/*** 导入文件** @param inputStream*/void importUserInfo(InputStream inputStream);
}
  • 导入导出业务逻辑代码
/*** @author itender* @date 2023/1/30 17:43* @desc*/
@Slf4j
@Service
public class UserServiceImpl implements UserService {@Overridepublic void exportUserInfo(ServletOutputStream outputStream) {// 第一种方式ExcelWriter excelWriter = EasyExcelFactory.write(outputStream).build();WriteSheet userSheet = EasyExcelFactory.writerSheet(0).head(User.class)// 导出文件需不包含的列名.excludeColumnFieldNames(Lists.newArrayList())// 导出文件包含的列名.includeColumnFieldNames(Lists.newArrayList()).build();excelWriter.write(this::getUserList, userSheet);excelWriter.finish();// 第二种方式EasyExcelFactory.write(outputStream, User.class).sheet("userInfo").doWrite(this::getUserList);}private List getUserList() {return Collections.singletonList(User.builder().id(1L).userName("itender").gender("男").address("广东深圳").email("itender@163.com").phoneNumber(13156777777L).description("hello world").build());}@Overridepublic void importUserInfo(InputStream inputStream) {// 第一种方式ExcelDataListener excelDataListener = new ExcelDataListener();ExcelReader excelReader = EasyExcelFactory.read(inputStream).build();ReadSheet userSheet = EasyExcelFactory.readSheet(0).head(User.class).registerReadListener(excelDataListener).build();excelReader.read(userSheet);// 第二种方式EasyExcelFactory.read(inputStream, User.class, new ReadListener() {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 缓存的数据*/private final List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Overridepublic void invoke(User user, AnalysisContext analysisContext) {cachedDataList.add(user);}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {cachedDataList.forEach(user -> log.info(user.toString()));}}).sheet().doRead();// 拿到错误信息,返回前端String errorMsg = excelDataListener.getErrorMsg();}
}
  • 导入文件监听器
/*** @author itender* @date 2023/1/31 12:21* @desc*/
@Slf4j
public class ExcelDataListener implements ReadListener {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;/*** 缓存的数据*/private static final List CACHED_DATA_LIST = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);/*** 错误信息*/@Getterprivate String errorMsg;/*** 这个每一条数据解析都会来调用** @param user* @param analysisContext*/@Overridepublic void invoke(User user, AnalysisContext analysisContext) {log.info("解析到一条数据:{}", JSONUtil.toJsonStr(user));// TODO 校验导入数据是否合规// 如果不合规this.errorMsg = StrFormatter.format("导入数据第{}行校验不通过!", analysisContext.readRowHolder().getRowIndex());CACHED_DATA_LIST.add(user);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (CACHED_DATA_LIST.size() >= BATCH_COUNT) {// TODO 保存数据到MySQL// 存储完成置空listCACHED_DATA_LIST.clear();}}/*** 所有数据解析完成了 都会来调用** @param analysisContext*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库// TODO 保存数据到MySQLlog.info("所有数据解析完成!");}
}

参考:

  • 官方网站:https://easyexcel.opensource.alibaba.com/
  • github地址:https://github.com/alibaba/easyexcel
  • gitee地址:https://gitee.com/easyexcel/easyexcel

3、问题

  1. 在很多场景下,Excel的列与实体类可能并不完全一致,这时就需要排除一些实体类的字段。

    方式一:类上加注解 @ExcelIgnoreUnannotated,过滤属性没有@ExcelProperty注解的字段

    方式二:指定字段加@ExcelIgnore注解

    方式三:代码指定过滤字段,通过excludeColumnFiledNames方法:

  2. 防止导出文件名乱码:

    response.setHeader(“Content-disposition”,
    “attachment;filename*=utf-8’zh_cn’” + fileName + System.currentTimeMillis() + “.xlsx”);

  3. 可以自定义Listener监听器实现导入数据校验,避免一次性导入太多数据,最好数据分批入库。

4、总结

本文介绍了EasyExcel的使用,整体而言操作简单、使用方便,提供了不少注解,方便与实体对象之间的关系绑定。而且官网也提供了相关的性能数据,更多的API使用大家还可以继续探索。

无论从性能或易用性上来说,都值得你尝试。特别是临时写一个Excel的解析或生成的工具,再也不用惆怅一行行的解析了,赶紧收藏用起来吧。

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
一帆风顺二龙腾飞三阳开泰祝福语... 本篇文章极速百科给大家谈谈一帆风顺二龙腾飞三阳开泰祝福语,以及一帆风顺二龙腾飞三阳开泰祝福语结婚对应...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...