天津网站大全,wordpress 中文下载,上海网站建设口碑最好的公司,广州冼村的人为什么这么有钱文章目录 一、easyExcel1.什么是easyExcel2.easyExcel示例demo3.easyExcel read的底层逻辑~~4.easyExcel write的底层逻辑~~ 二、FastExcel1.为什么更换为fastExcel2.fastExcel新功能 一、easyExcel
1.什么是easyExcel 内容摘自官方#xff1a;Java解析、生成Excel比较有名的… 文章目录 一、easyExcel1.什么是easyExcel2.easyExcel示例demo3.easyExcel read的底层逻辑~~4.easyExcel write的底层逻辑~~ 二、FastExcel1.为什么更换为fastExcel2.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.class和PersonVO.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())) {// 读取数据ListPersonVo excelDatas new ArrayList();EasyExcel.read(is, Person.class, new CustomPageReadListenerPerson(dataList - {if (CollectionUtils.isEmpty(dataList)) {return;}dataList.forEach(data - {PersonVo personVo new PersonVo();BeanUtils.copyProperties(data, personVo);excelDatas.add(personVo);});})).sheet().doReadSync();// 为了实现自定义表格样式,根据ifOffer来决定行颜色MapInteger, 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,是实际开发中这三个是最常用的工具
OfferEnumConverter: String – Enum转换器,实现supportJavaTypeKey及supportExcelTypeKey是为了在Easy.registerConverter()注册通用转换器也可以使用
## OfferEnumConverter.class
public class OfferEnumConverter implements ConverterOfferEnum {Overridepublic ClassOfferEnum 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;}
}CustomPageReadListener: 监听器是在读取完一行数据后被调用的,invoke中接收到的是一行的数据。这里做了处理空行的操作,虽然EasyExcel默认情况下会配置ignoreEmptyRow为true,但是如果行内某个单元格无数据但有单元格式,会被EasyExcel认为非空行,因此对空行严谨的项目需要在这里处理一下空行。
public class CustomPageReadListenerT extends PageReadListenerT {public CustomPageReadListener(ConsumerListT consumer) {super(consumer);}Overridepublic void invoke(T data, AnalysisContext context) {// 处理空行if (isNullLine(data)) {return;}// 特殊字段赋值及处理如dateStr赋值给dateflushData(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;}
}CustomCellWriteHandler: 将内存中的数据写入excel时,需要做一些特殊处理时(如:脱敏处理、添加单元格样式、合并单元格等),可以通过实现WriteHandler来实现功能,demo中只有添加单元格样式,官方文档中有很全面的各种案例用法
public class CustomCellWriteHandler implements CellWriteHandler {private final MapInteger, Short cellColorType;public CustomCellWriteHandler(MapInteger, 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数据及导出后的效果参照下图:
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基于 SAXSimple API for XML事件驱动模型实现高效的大文件流式解析避免内存溢出OOMXlsxRowHandler重写了startElement来实现对每一行每一个单元格的读取。当所有XlsxTagHandler执行完后开始endElement进行cell类型的转换等最终交给AnalysisEventProcessor.endRow来处理数据并调用ReadListener监听器来对数据做处理如PageReadListener来缓存数据EasyExcel有四个解析excel的入口,分别为 .sheet().doRead() – sheet中不加参数,则默认取sheetNo为0的sheet,doRead中进行解析excel.sheet().doReadSync() – 相对doRead(),注册了一个新的Listener用来缓存数据读取excel结束后直接从Listner中读取数据并returndoReadAll() – 顾名思义,读取所有的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用户可以最低成本过度到fastExcelfastExcel通过对底层算法的优化和内存管理的改进能更高效的处理大规模的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 PageReadListenerPerson(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);