EasyExcel详解

文章目录

    • 一、easyExcel
      • 1.什么是easyExcel
      • 2.easyExcel示例demo
      • 3.easyExcel read的底层逻辑
      • ~~4.easyExcel write的底层逻辑~~
    • 二、FastExcel
      • 1.为什么更换为fastExcel
      • 2.fastExcel新功能

一、easyExcel

1.什么是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模式,在上层做了模型转换的封装,让使用者更加简单方便

通俗解释就是说:一个基于poi的excel简化开发包,性能比poi要好,且易于使用

官方文档地址
源码地址

2.easyExcel示例demo

官方文档非常全面,本无需写一个demo来记录。本demo旨在展示easyExcel的读写基础用法、自定义类型转换、自定义单元格格式及excel空白行处理等,可以理解为将常用的情况记录下来,省去看官方文档的时间。

## PersonVO.class,代码中的Person.classPersonVO.class的区别为没有ifOffer字段,为了展示而做了区分
## Person.class是用来读excel的,PersonVO.class用来写excel
@Data
public class PersonVo {@ExcelProperty("名称")private String name;@ExcelProperty("性别")private String gender;@ExcelProperty("年龄")private Integer age;@ExcelProperty("信息")private String info;@ExcelProperty("评分")private Float score;// OfferEnumConverter为自定义的Converter,用来做OfferEnum和String的映射@ExcelProperty(value = "是否录用", converter = OfferEnumConverter.class)private OfferEnum ifOffer;
}## excel读及写部分,如果read时使用PersonVo.class映射表头
## 则可以在CustomPageReadListener.class的invoke方法中,做对person.ifOffer的赋值File file = new File("D:\\develop\\work\\test.xlsx");
try (InputStream is = Files.newInputStream(file.toPath())) {// 读取数据List<PersonVo> excelDatas = new ArrayList<>();EasyExcel.read(is, Person.class, new CustomPageReadListener<Person>(dataList -> {if (CollectionUtils.isEmpty(dataList)) {return;}dataList.forEach(data -> {PersonVo personVo = new PersonVo();BeanUtils.copyProperties(data, personVo);excelDatas.add(personVo);});})).sheet().doReadSync();// 为了实现自定义表格样式,根据ifOffer来决定行颜色Map<Integer, Short> cellColorType = new HashMap<>();for (int i = 0; i < excelDatas.size(); i++) {PersonVo person = excelDatas.get(i);if (person.getScore() > 3) {person.setIfOffer(OfferEnum.OFFER);cellColorType.put(i + 1, IndexedColors.GREEN.getIndex());} else if (person.getScore() < 2) {person.setIfOffer(OfferEnum.REFUSE);cellColorType.put(i + 1, IndexedColors.RED.getIndex());} else {person.setIfOffer(OfferEnum.WAIT);cellColorType.put(i + 1, IndexedColors.YELLOW.getIndex());}}EasyExcel.write("D:\\develop\\work\\test1.xlsx", PersonVo.class).registerWriteHandler(new CustomCellWriteHandler(cellColorType)).sheet("测试").doWrite(excelDatas);
} catch (IOException e) {throw new RuntimeException(e);
}

demo中用到了自定义类型转换OfferEnumConverter、自定义excel读取监听器CustomPageReadListener、自定义WriteHandler CustomCellWriteHandler,是实际开发中这三个是最常用的工具

  1. OfferEnumConverter: String <–> Enum转换器,实现supportJavaTypeKey及supportExcelTypeKey是为了在Easy.registerConverter()注册通用转换器也可以使用
## OfferEnumConverter.class
public class OfferEnumConverter implements Converter<OfferEnum> {@Overridepublic Class<OfferEnum> supportJavaTypeKey() {return OfferEnum.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic OfferEnum convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {return OfferEnum.valueOf(cellData.getStringValue());}@Overridepublic WriteCellData<?> convertToExcelData(OfferEnum value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {if(Objects.isNull(value)) {return new WriteCellData<>("");} else {return new WriteCellData<>(value.getValue());}}
}## OfferEnum.class 录用标记枚举
@Getter
public enum OfferEnum {OFFER("y", "录用"),REFUSE("n", "不录用"),WAIT("wait", "待定");;private final String value;private final String desc;OfferEnum(String value, String desc) {this.value = value;this.desc = desc;}public static OfferEnum getByValue(String value) {for (OfferEnum offerEnum : OfferEnum.values()) {if (offerEnum.value.equals(value)) {return offerEnum;}}return WAIT;}
}
  1. CustomPageReadListener: 监听器是在读取完一行数据后被调用的,invoke中接收到的是一行的数据。这里做了处理空行的操作,虽然EasyExcel默认情况下会配置ignoreEmptyRow为true,但是如果行内某个单元格无数据但有单元格式,会被EasyExcel认为非空行,因此对空行严谨的项目需要在这里处理一下空行。
public class CustomPageReadListener<T> extends PageReadListener<T> {public CustomPageReadListener(Consumer<List<T>> consumer) {super(consumer);}@Overridepublic void invoke(T data, AnalysisContext context) {// 处理空行if (isNullLine(data)) {return;}// 特殊字段赋值及处理(如:dateStr赋值给date)flushData(data);// 处理数据转换异常super.invoke(data, context);}private void flushData(T data) {}private boolean isNullLine(T data) {System.err.println(JSON.toJSONString(data));// 获取data每个字段,反射判断是不是都为空或空字符串for (Field field : data.getClass().getDeclaredFields()) {field.setAccessible(true);try {Object value = field.get(data);if (value instanceof String) {if (!StringUtils.isEmpty(value)) {return false;}} else {if (Objects.nonNull(value)) {return false;}}} catch (IllegalAccessException e) {return false;}}return true;}
}
  1. CustomCellWriteHandler: 将内存中的数据写入excel时,需要做一些特殊处理时(如:脱敏处理、添加单元格样式、合并单元格等),可以通过实现WriteHandler来实现功能,demo中只有添加单元格样式,官方文档中有很全面的各种案例用法
public class CustomCellWriteHandler implements CellWriteHandler {private final Map<Integer, Short> cellColorType;public CustomCellWriteHandler(Map<Integer, Short> cellColorType) {if(Objects.isNull(cellColorType)) {cellColorType = new HashMap<>();}this.cellColorType = cellColorType;}@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {// 表头样式不变if (BooleanUtils.isNotTrue(context.getHead())) {int rowIndex = context.getRowIndex();Short colorIndex = cellColorType.get(rowIndex);if(Objects.nonNull(colorIndex)) {WriteCellData<?> cellData = context.getFirstCellData();// 这里需要去cellData 获取样式// 很重要的一个原因是 WriteCellStyle 和 dataFormatData绑定的 简单的说 比如你加了 DateTimeFormat// ,已经将writeCellStyle里面的dataFormatData 改了 如果你自己new了一个WriteCellStyle,可能注解的样式就失效了// 然后 getOrCreateStyle 用于返回一个样式,如果为空,则创建一个后返回WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();writeCellStyle.setFillForegroundColor(colorIndex);// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUNDwriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);}}}
}

本案例使用的test.excel数据及导出后的效果参照下图:
Excel测试数据
WriteExcel结果

3.easyExcel read的底层逻辑

通过ExcelAnalyser来配置excel解析执行器

  • 通过FileMagic来读取文件开头几个字节的魔数,以确定文件的类型。为了兼容CSV文件,通过File方式readExcel的时候,通过判断文件的后缀名称是否为.csv来判断是否为CSV文件
  • 设置read上下文:解析表头,加载readListener、Converter(预定义的Converter和通过registerConverter注册的Converter)、设置忽略空行(如果空行中有表格样式,则无法忽略)及readCache
  • 设置read执行器:选择合适的执行器,并加载所有的sheet。这里加载了所有的sheet,在read的时候会根据条件选择要读取的sheet

通过ExcelAnalyser.analysis来解析excel

  • 从xlsx视角出发的,xls和csv这里不做展示
  • XlsxSaxAnalyser.parseXmlSource()中使用SAXParserFactory来解析 Excel 文件底层 XML 结构。SAXParserFactory基于 SAX(Simple API for XML)事件驱动模型实现高效的大文件流式解析,避免内存溢出(OOM)
  • XlsxRowHandler重写了startElement来实现对每一行每一个单元格的读取。当所有XlsxTagHandler执行完后,开始endElement进行cell类型的转换等,最终交给AnalysisEventProcessor.endRow来处理数据,并调用ReadListener监听器来对数据做处理(如PageReadListener来缓存数据)
  • EasyExcel有四个解析excel的入口,分别为
    • .sheet().doRead() – sheet中不加参数,则默认取sheetNo为0的sheet,doRead中进行解析excel
    • .sheet().doReadSync() – 相对doRead(),注册了一个新的Listener用来缓存数据,读取excel结束后直接从Listner中读取数据并return
    • doReadAll() – 顾名思义,读取所有的sheet(),并映射到同一个实体list中,适合同类型分页数据
    • .doReadAllSync() – 同上
  • 读取excel的关键为SAXParserFactory和ReadCache,具体逻辑可以自己阅读源码,或使用AI工具辅助阅读

4.easyExcel write的底层逻辑


二、FastExcel

文本采用的fastExcel版本为1.0.0,当前时间最新版本为1.2.0

目前FastExcel官网已挂,仅有开源源码地址

1.为什么更换为fastExcel

  • 2024年8月阿里已宣布停止更新easyExcel,同时原作者宣布新开发fastExcel,支持所有easyExcel的功能,因此原easyExcel用户可以最低成本过度到fastExcel
  • fastExcel通过对底层算法的优化和内存管理的改进,能更高效的处理大规模的excel数据,大幅降低内存消耗和处理时间
  • 新功能:读取excel指定行数,excel转pdf(注意:仅仅是将excel文件转为pdf文件,且在1.1.0版本中已经移除此功能,谨慎使用

2.fastExcel新功能

## fastExcel中既可以用FastExcel.class,也可以用EasyExcel.class,除了1.0.0版本外,俩完全一样
## .numRows()即读取excel指定行数,.numRows(10)即从表头开始读10,上文中的案例,就只会读到9条数据
FastExcel.read(is, Person.class, new PageReadListener<Person>(dataList -> {if (CollectionUtils.isEmpty(dataList)) {return;}dataList.forEach(data -> {PersonVo personVo = new PersonVo();BeanUtils.copyProperties(data, personVo);excelDatas.add(personVo);});
})).sheet().numRows(10).doRead();## excel文件转为pdf文件,谨慎使用
FastExcel.convertToPdf(new File("D:\\develop\\work\\test1.xlsx"), new File("D:\\develop\\work\\test2.pdf"), null, null);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/906019.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

jvm安全点(三)openjdk17 c++源码垃圾回收之安全点结束,唤醒线程

1. VMThread::inner_execute() - 触发安全点​​ cpp 复制 void VMThread::inner_execute(VM_Operation* op) { if (op->evaluate_at_safepoint()) { SafepointSynchronize::begin(); // 进入安全点&#xff0c;阻塞所有线程 // ...执行GC等操作... SafepointSynchronize::…

102. 二叉树的层序遍历递归法:深度优先搜索的巧妙应用

二叉树的层序遍历是一种经典的遍历方式&#xff0c;它要求按层级逐层访问二叉树的节点。通常我们会使用队列来实现层序遍历&#xff0c;但递归法也是一种可行且有趣的思路。本文将深入探讨递归法解决二叉树层序遍历的核心难点&#xff0c;并结合代码和模拟过程进行详细讲解。 …

首个窗口级无人机配送VLN系统!中科院LogisticsVLN:基于MLLM实现精准投递

导读 随着智能物流需求日益增长&#xff0c;特别是“最后一公里”配送场景的精细化&#xff0c;传统地面机器人逐渐暴露出适应性差、精度不足等瓶颈。为此&#xff0c;本文提出了LogisticsVLN系统——一个基于多模态大语言模型的无人机视觉语言导航框架&#xff0c;专为窗户级别…

WPF Datagrid 数据加载和性能

这篇文章并非讨论 WPF Datagrid 的性能数据&#xff0c;而只是简单介绍一下为了使其性能良好&#xff0c;你需要注意哪些方面。我不太想使用性能分析器来展示实际数据&#xff0c;而是尽可能地使用了 Stopwatch 类。这篇文章不会深入探讨处理海量数据的技术&#xff0c;例如分页…

matlab求矩阵的逆、行列式、秩、转置

inv - 计算矩阵的逆 用途&#xff1a;计算一个可逆矩阵的逆矩阵。 D [1, 2; 3, 4]; % 定义一个2x2矩阵 D_inv inv(D); % 计算矩阵D的逆 disp(D_inv);det - 计算矩阵的行列式 用途&#xff1a;计算方阵的行列式。 E [1, 2; 3, 4]; determinant det(E); % 计算行列式 disp…

ridecore流水线解读

文章目录 流水线stage分属前后端PCpipelineIFIDDPDP 与 SW 中间没有latchSWCOM 源码地址 流水线stage分属前后端 IF -> ID -> DP -> SW -> EX -> COM分类阶段说明前端IF指令获取阶段。PC 使用分支预测器&#xff0c;访问指令存储器。典型前端操作。前端ID解码并…

【SpringBoot】关于MP使用中配置了数据库表前缀的问题

problem 使用MP时&#xff0c;在application.yml配置文件中配置了MP匹配数据库表中的表名时的前缀作了规定&#xff0c;如下&#xff1a; 那么当我运行时报错了错误&#xff0c;报错信息如下&#xff1a; 因为我数据库表的书类表名是book&#xff0c;MP在匹配时使用了表名前…

印度Rummy游戏支付通道申请策略:技巧类游戏的合规与创新

本文为印度支付申请科普文&#xff0c;自去年开始&#xff0c;印度Rummy类游戏申请印度支付都需要拥有AIGF的会员及产品证书。 如需要rummy可以通过AIGF审核的源。码&#xff0c;或咨询AIGF的相关内容&#xff0c;可以联。系老妙。 印度作为全球棋牌类游戏增长最快的市场之一&…

日志与策略模式

什么是设计模式 IT⾏业 ,为了让 菜鸡们不太拖⼤佬的后腿, 于是⼤佬们针对⼀些经典的常⻅的场景, 给定了⼀些对应的解决⽅案, 这个就是 设计模式 日志认识 计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件&#xff0c;主要作⽤是监控运⾏状态、记录异常信 息&#xff…

解锁Ubuntu高效部署!自动安装配置文件YAML全解析

我们之前介绍了两种Ubuntu系统的安装方式&#xff0c;分别对应桌面版&#xff08;准备搞OpenStack了&#xff0c;先装一台最新的Ubuntu 23.10&#xff09;和服务器版&#xff08;Ubuntu 22.04 LTS服务器版本安装演示&#xff09;。但对于有些用户&#xff0c;因为技术问题&…

关系代数和关系数据库语言(SQL)

阅读提示&#xff1a;本篇文章较长&#xff0c;建议从目录上选取想看的内容。代码上的话&#xff0c;我习惯用小写&#xff0c;如果看不习惯建议跳过。有问题欢迎讨论&#xff01;&#xff01;&#xff01; 一、基础概念 1.1数据库的概念 数据库(Database)是按照数据结构来组…

EXO 可以将 Mac M4 和 Mac Air 连接起来,并通过 Ollama 运行 DeepSeek 模型

EXO 可以将 Mac M4 和 Mac Air 连接起来&#xff0c;并通过 Ollama 运行 DeepSeek 模型。以下是具体实现方法&#xff1a; 1. EXO 的分布式计算能力 EXO 是一个支持 分布式 AI 计算 的开源框架&#xff0c;能够将多台 Mac 设备&#xff08;如 M4 和 Mac Air&#xff09;组合成…

区块链基本理解

文章目录 前言一、什么是分布式账本(DLT)二、什么是P2P网络?二、共识算法三、密码算法前言 区块链是由一个一个数据块组成的链条,按照时间顺序将数据块逐一链接,通过哈希指针链接,所有的数据块共同维护一份分布式账本(DLT),每个节点(可以理解为一个玩家,一台计算机)都拥…

Node.js中的洋葱模型

文章目录 前言 前言 Node.js中的洋葱模型是一种中间件执行机制&#xff0c;主要用于处理HTTP请求和响应的流程控制。该模型通过层层包裹的中间件结构&#xff0c;实现请求从外到内穿透、响应从内向外返回的顺序执行。以下从核心概念、实现原理、框架差异及实际应用等方面解析&…

UI-TARS Desktop:用自然语言操控电脑,AI 重新定义人机交互

在人工智能技术飞速发展的今天,从文本生成到图像识别,AI 的能力边界不断被打破。而字节跳动近期开源的 UI-TARS Desktop,则将这一技术推向了更复杂的交互场景——通过自然语言直接控制计算机界面,实现了图形用户界面(GUI)的智能化自动化。这款工具不仅降低了操作门槛,更…

一个可拖拉实现列表排序的WPF开源控件

从零学习构建一个完整的系统 推荐一个可通过拖拉&#xff0c;来实现列表元素的排序的WPF控件。 项目简介 gong-wpf-dragdrop是一个开源的.NET项目&#xff0c;用于在WPF应用程序中实现拖放功能&#xff0c;可以让开发人员快速、简单的实现拖放的操作功能。 可以在同一控件内…

C语言中字符串函数的详细讲解

C语言提供了丰富的字符串处理函数&#xff0c;这些函数在<string.h>头文件中声明。以下是一些常用字符串函数的详细讲解&#xff1a; 字符串拷贝函数 strcpy 功能&#xff1a;将源字符串&#xff08;包括结尾的\0&#xff09;复制到目标字符串。原型&#xff1a;char *s…

可视化数据图表怎么做?如何实现三维数据可视化?

目录 一、三维数据可视化的要点 1. 明确数据可视化的目标 2. 筛选与整理数据 3. 选择合适的图表类型 4. 运用专业工具制作 5. 优化图表的展示效果 二、数据可视化图表怎么做&#xff1f; 1. 理解三维数据的特性 2. 数据处理与三维建模 3. 设置光照与材质效果 4. 添加…

在Linux服务器上部署Jupyter Notebook并实现ssh无密码远程访问

Jupyter notebook版本7.4.2&#xff08;这个版本AI提示我Jupyter7&#xff08;底层是 jupyter_server 2.x&#xff09; 服务器开启服务 安装Jupyter notebook 7.4.2成功后&#xff0c;终端输入 jupyter notebook --generate-config 这将在 ~/.jupyter/ 目录下生成 jupyter_…

走出 Demo,走向现实:DeepSeek-VL 的多模态工程路线图

目录 一、引言&#xff1a;多模态模型的关键转折点 &#xff08;一&#xff09;当前 LMM 的三个关键挑战 1. 数据的真实性不足 2. 模型设计缺乏场景感知 3. 语言能力与视觉能力难以兼顾 &#xff08;二&#xff09;DeepSeek-VL 的根本出发点&#xff1a;以真实任务为锚点…