阿里的easyexcal包实现表格动态导出

1.介绍
在日常开发中,我们或多或少会遇到导入excal,导出excal等业务需求,那么了解这一技能就很有必要了。
市场中针对这个,我知道的有两个包,一个是poi(Poor Obfuscation Implementation),一个是easyexcal(阿里的);但poi存在oom内存溢出的风险,在正式环境中,导出的数据往往成千上万条,很容易就触发oom,所以,一般情况下公司都不建议使用poi,阿里的easyexcal包的底层实现也是使用了poi,也就是说easyexcal是对poi的进一步封装改造,成功规避了oom。
2.场景需求
现在有一个需求:实现表头信息动态,表数据动态,表中单元格具有各自的样式(比如背景颜色,字体样式,字体颜色等)。
3.设计方案思路
我这里使用easyexcal包实现该需求,首先注意表头信息是变动的,那也就意味着没法使用easyexcal的一系列注解(就是常规的先创建一个导出的实体类,然后再加注解),这里有个坑:我最开始的想法是,创建包含所有表头信息的导出实体,然后通过反射的技术对具体需要展示的表头字段进行标识,然后发现easyexcal中的注解@ExcelIgnore 忽略项是没有value属性的,这样就出现了尴尬的情况:反射能给字段添加或删除注解吗???,我查了很多资料,最后是没找到,应该不能实现添加或删除,但给注解中的属性值修改是可以做到的,但@ExcelIgnore没有属性供我们修改,至此,该方法行不通了;然后我看了easyexcal源码,大致知道了导出excal的流程:先渲染表头,再渲染数据,他们都是一个个单元格,每一个单元格有自己的行列数,一个excal表就是由一个个单元格以此拼接起来的。于是,我就使用单独给表头数据,表体数据,需要给单元格加样式的行列坐标数据和样式数据。需要注意的是表头和表体数据格式是二维数组,我是用list中套list,外层list中的元素是一行,内部list中的元素是列。

4.具体代码实现
我这里分别使用了3.0.5和2.2.8的easyexcal包,需要注意的是3.0版本之前和之后有较大改动,其实现的方法不一样,注意:以下案例在springboot项目中实现
4.1easyexcal3.0.5版本实现代码
4.1.1导核心依赖

 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency>

4.1.2写一个类继承AbstractCellWriteHandler或者也可以实现CellWriteHandler接口

public class MyCellStyleWriteHandler extends AbstractCellWriteHandler {private List<XyInfo> xyInfo;/*给样式模板两个*/private CellStyle style1;private CellStyle style2;public MyCellStyleWriteHandler(){}public MyCellStyleWriteHandler(List<XyInfo> xyInfo){this.xyInfo=xyInfo;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {// 设置行高测试}@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {Cell cell = context.getCell();// 拿到poi的workbookWorkbook workbook = context.getWriteWorkbookHolder().getWorkbook();// 自定义宽度处理// 自定义样式处理// 当前事件会在 数据设置到poi的cell里面才会回调// 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not trueif (BooleanUtils.isNotTrue(context.getHead())) {//循环样式信息,进行横纵坐标的匹配,给对应的单元格样式for (XyInfo item:xyInfo) {if (cell.getRowIndex() == item.getX() && cell.getColumnIndex() == item.getY()) {//现在已经锁定的单元格,下面只需要给样式//样式: 1:红字,紫色背景;2:黄字,红色背景// 这里千万记住 想办法能复用的地方把他缓存起来 一个表格最多创建6W个样式,我这就直接样式模板化,避免其发生if(item.getContent()==1){cell.setCellStyle(this.getCellStyle(workbook,1));}if(item.getContent()==2){cell.setCellStyle(this.getCellStyle(workbook,2));}// 由于这里没有指定dataformat 最后展示的数据 格式可能会不太正确// 这里要把 WriteCellData的样式清空, 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到// cell里面去 会导致自己设置的不一样(很关键)context.getFirstCellData().setWriteCellStyle(null);}}}}//优化代码,给个样式方法/***  对于可确定的样式,进行样式模板化,避免每一个单元格都创建一个样式* @param workbook 工作簿对象* @param content 具体设置样式信息* @return*/public CellStyle getCellStyle(Workbook workbook,Integer content){//样式: 1:红字,紫色背景;2:黄字,红色背景if(null==this.style1 && content==1){//避免多次创建//这里有个坑:我最初的想法是自己创建CellStyle 对象,通过new的方式,然后这个地方不认他,设置完全不起效果,所以这里只能从weekbook中拿。style1=workbook.createCellStyle();Font font=workbook.createFont();//红字font.setColor((short)016);//蓝色背景style1.setFillForegroundColor((short)030);//加载字体style1.setFont(font);// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUNDstyle1.setFillPattern(FillPatternType.SOLID_FOREGROUND);}if(null==style2 && content==2){//避免多次创建style2=workbook.createCellStyle();Font font=workbook.createFont();//白字font.setColor((short)011);//红色背景style2.setFillForegroundColor((short)016);//加载字体style2.setFont(font);// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUNDstyle2.setFillPattern(FillPatternType.SOLID_FOREGROUND);}//返回样式模板if(content==1){return this.style1;}if (content==2){return this.style2;}return null;}
}

4.1.3导出关键代码

public class Test9 {public static void main(String[] args) {//你要导出的文件存放路径和文件名字String filePath = "D:\\ttt\\Download\\";String fileName=System.currentTimeMillis() + ".xlsx";File file = new File(filePath);if (!file.exists()){file.mkdirs();}//解析表头容器List<List<String>>headss=new LinkedList<>();//解析数据容器List<List<String>>datas=new LinkedList<>();//解析单元格样式容器List<XyInfo>xyInfo=new LinkedList<>();//手动添加假数据模拟真实数据(表头信息)headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("A","AA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("B","AAA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("C","A2","A3","A4"))));//手动添加假数据模拟真实数据(标体数据)datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","")));datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","ccc")));datas.add(new LinkedList<String>(Arrays.asList("aaa","","")));//手动添加需要设置样式的单元格信息xyInfo.add(new XyInfo(6,0,1));xyInfo.add(new XyInfo(5,1,2));EasyExcel.write(filePath+fileName)// 这里放入动态头.head(headss).sheet("模板(sheet名字)")//加载单元格样式.registerWriteHandler(new MyCellStyleWriteHandler(xyInfo)).doWrite(datas);System.out.println("导出成功");}
}

XyInfo实体类

@Data
public class XyInfo {/*** 行*/private Integer x=0;/*** 列*/private Integer y=0;/***   样式: 1:红字,紫色背景;2:黄字,红色背景*/private Integer content;public XyInfo(Integer x,Integer y,Integer content){this.x=x;this.y=y;this.content=content;}
}

4.1.4效果展示


4.2easyexcal2.2.8版本实现代码
4.2.1导核心依赖

        <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.8</version></dependency>

4.2.2写一个类实现CellWriteHandler接口

public class My2 implements CellWriteHandler {private List<XyInfo> xyInfo;/*给样式模板两个*/private CellStyle style1;private CellStyle style2;public My2(List<XyInfo> xyInfo){this.xyInfo=xyInfo;}/*** 在创建单元格之前调用*/@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {}/*** 在单元格创建后调用*/@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {}/*** 在单元上的所有操作完成后调用*/@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {// 拿到poi的workbookWorkbook workbook = cell.getSheet().getWorkbook();// 自定义宽度处理// 自定义样式处理// 当前事件会在 数据设置到poi的cell里面才会回调// 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true//循环样式信息,进行横纵坐标的匹配,给对应的单元格样式for (XyInfo item:xyInfo) {if (cell.getRowIndex() == item.getX() && cell.getColumnIndex() == item.getY()) {//现在已经锁定的单元格,下面只需要给样式//样式: 1:红字,紫色背景;2:黄字,红色背景// 这里千万记住 想办法能复用的地方把他缓存起来 一个表格最多创建6W个样式,我这就直接样式模板化,避免其发生if(item.getContent()==1){cell.setCellStyle(this.getCellStyle(workbook,1));}if(item.getContent()==2){cell.setCellStyle(this.getCellStyle(workbook,2));}}}}//优化代码,给个样式方法/***  对于可确定的样式,进行样式模板化,避免每一个单元格都创建一个样式* @param workbook 工作簿对象* @param content 具体设置样式信息* @return*/public CellStyle getCellStyle(Workbook workbook,Integer content){//样式: 1:红字,紫色背景;2:黄字,红色背景if(null==this.style1 && content==1){//避免多次创建style1=workbook.createCellStyle();Font font=workbook.createFont();//红字font.setColor((short)016);//蓝色背景style1.setFillForegroundColor((short)030);//加载字体style1.setFont(font);// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUNDstyle1.setFillPattern(FillPatternType.SOLID_FOREGROUND);}if(null==style2 && content==2){//避免多次创建style2=workbook.createCellStyle();Font font=workbook.createFont();//白字font.setColor((short)011);//红色背景style2.setFillForegroundColor((short)016);//加载字体style2.setFont(font);// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUNDstyle2.setFillPattern(FillPatternType.SOLID_FOREGROUND);}//返回样式模板if(content==1){return this.style1;}if (content==2){return this.style2;}return null;}
}

4.2.3导出关键代码

public class Test10 {public static void main(String[] args) {//你要导出的文件存放路径和文件名字String filePath = "D:\\ttt\\Download\\";String fileName=System.currentTimeMillis() + ".xlsx";File file = new File(filePath);if (!file.exists()){file.mkdirs();}//解析表头容器List<List<String>>headss=new LinkedList<>();//解析数据容器List<List<String>>datas=new LinkedList<>();//解析单元格样式容器List<XyInfo>xyInfo=new LinkedList<>();//手动添加假数据模拟真实数据(表头信息)headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("A","AA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("B","AAA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("C","A2","A3","A4"))));//手动添加假数据模拟真实数据(标体数据)datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","")));datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","ccc")));datas.add(new LinkedList<String>(Arrays.asList("aaa","","")));//手动添加需要设置样式的单元格信息xyInfo.add(new XyInfo(6,0,1));xyInfo.add(new XyInfo(5,1,2));EasyExcel.write(filePath+fileName)// 这里放入动态头.head(headss).sheet("模板(sheet名字)")//加载单元格样式.registerWriteHandler(new My2(xyInfo)).doWrite(datas);System.out.println("导出成功");}
}

4.2.3效果展示


5.总结
到此,该需求基本实现,现在就简单说说easyexcal包中的技术,使用了拦截器技术,aop思想,动态代理等,具体的,我后续会做整理,目前还在研究源码,最后希望此文章能给你带来灵感。

阿里的easyexcal包实现表格动态导出相关推荐

  1. Java多级动态导出表格,优化版

    动态导出表格 接上次发布的导出做了一些优化和加入一些功能 优化:修改了一些bug,和优化判断逻辑 新功能:加入样式自定义,可自定义表头和表内容样式 新方法方法名为:summaryTableExport ...

  2. java导出excel压缩包_java动态导出excel压缩成zip下载的方法

    本文实例为大家分享了java动态导出excel压缩成zip下载的具体代码,供大家参考,具体内容如下 package pack.java.io.demo; import java.io.Buffered ...

  3. java POI对word中的表格动态插入固定数据,以及插入不确定数量的的数据

    java POI对word中的表格动态插入固定数据,以及插入不具体的数据 遇到个项目本来是用Execl导出的,相对简单,客户要求用Word导出,并按照他们给的模板进行导出: 从网上百度了一下,然后自己 ...

  4. ie如何导入html文件类型,Magicodes.IE: 导入导出通用库,支持Dto导入导出以及动态导出,支持Excel、Word、Pdf和Html。...

    Magicodes.IE 导入导出通用库,支持Dto导入导出以及动态导出,支持Excel.Word.Pdf和Html. 疯狂的徽章 GitHub Azure DevOps Build Status: ...

  5. html表格中添加修改和删除链接,jQuery实现为table表格动态添加或删除tr功能示例...

    本文实例讲述了jQuery实现为table表格动态添加或删除tr功能.分享给大家供大家参考,具体如下: HTML页面元素如下: 订单合同号 捆包号 品名 规格 材质 重量 业务需求是,从后台获取到订单 ...

  6. Excel模板导出之动态导出

    说明 目前Magicodes.IE已支持Excel模板导出时使用JObject.Dictionary和ExpandoObject来进行动态导出,具体使用请看本篇教程. 本功能的想法.部分实现初步源于a ...

  7. Excel表格导入导出功能实现

    ## 01.员工管理-Excel导入功能介绍 ### 目标 在前面员工的添加是一个一个进行的,如果一次性添加多个员工信息,这时候就会很繁琐 因此需要我们开发一个批量导入的功能,将用户的信息存储到 ex ...

  8. POI动态导出多层表头的EXCEL文件

    POI动态导出多层表头的EXCEL文件 表格表头导出 单行表头 多行表头 以前接触过一个很古老的导出Excel,实现的逻辑是先声明一个导出的Excel模板,模板里报表的表头名称和顺序是固定的,这样执行 ...

  9. 工作系列Java开发之利用Java实现ERP系统中Excel表格的导出

    工欲善其事必先利其器 前言: 最好的学习方式就是分享,喜欢就三连一下吧(关注,转发,点赞). 今天的内容开始之前,我先申明一下,今天的知识很重要! 很重要! 很重要 ,Excel表格的导出在正式步入公 ...

最新文章

  1. 轻松掌握移动端web开发【尺寸适配】常用解决方案
  2. 《HelloGitHub》第 20 期
  3. 付力力: 基于 ImpalaS 构建实时用户行为分析引擎
  4. BZOJ5212 ZJOI2018历史(LCT)
  5. 使用Eclipse创建一个Android程序方法
  6. 使用EasyUI的插件前需要引入的文件
  7. Shiro 权限注解
  8. The 7th Zhejiang Provincial Collegiate Programming Contest-Problem B:B - Somali Pirates
  9. IDEA启动报错:Error launching IDEA if you already have a 64-bit JDK installed,define a JAVA_HOME
  10. 超强,废弃手机用来做服务器,不用root,外网可访问!
  11. 背单词App开发日记2
  12. 安卓开发——android8.0应用崩溃,报错: Only fullscreen opaque activities can request orientation
  13. laravel 将汉字转化成拼音
  14. 出租车数据的地图展示
  15. Java百钱百鸡程序代码
  16. 网易企业邮箱:三道防御、七项措施,切实保障企业邮箱安全【企业邮箱申请】
  17. 情人节表白专用小系统
  18. 一个低边 MOS 管驱动电路的分析
  19. pygame网络游戏_4_2:人物行走_角色移动效果
  20. 【zed相机的简单使用】(2)

热门文章

  1. 将faster RCNN从github上下载的frozen模型转换为tflite时候报错
  2. 近两年NLP结合上下文的回复生成论文整理(含原文地址及数据集地址)
  3. 厉害了!打工人用Python分析西安市职位信息
  4. 后台密码加盐存储 pwd+salt sha256(),java实现
  5. 浅谈机器视觉系统的构成
  6. 升级nextcloud说明
  7. php 图片压缩 保留exif,保留图片原有的EXIF
  8. 23届银行春招:中国邮政储蓄银行面试真题分析!
  9. vue项目搭建,启动
  10. 计算机视觉任务怎么选?CV主要任务是什么?