EasyExcel模板导出(行和列自动合并)

1.需求背景:
①需要从第三方获取数据,第三方接口有两个参数,开始时间和结束时间

②获取回来的数据并没有入库,所以不能通过数据库将数据归类统计,excel合并大概的流程是判断上一行或者左右相邻列是否相同,然后进行合并,所以不能是零散的数据且客户要求每一个自治区和每一个航站要统计总数(后续会出一个数据整合文章),咱们默认数据已经整理好了.效果如下:
在这里插入图片描述
③最终效果:
在这里插入图片描述
2.初步实现:
①利用easyExcel模板填充,实现效果如下图
在这里插入图片描述

代码:

//模板位置InputStream template = new PathMatchingResourcePatternResolver().getResource("templates/飞机扑救火场统计表.xlsx").getInputStream();response.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码response.setHeader("Content-Disposition","attachment;filename=" + java.net.URLEncoder.encode("飞机扑救火场统计表.xlsx", "UTF-8"));//ExcelWriter该对象用于通过POI将值写入ExcelExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(template).build();//构建excel的sheet         WriteSheet writeSheet = EasyExcel.writerSheet().build();Map<String, String> fileData = new HashMap<>();fileData.put("beginDate", beginDate);fileData.put("endDate", endDate);excelWriter.fill(list, writeSheet);excelWriter.fill(fileData, writeSheet);excelWriter.finish();

模板:
在这里插入图片描述
3.列合并
列合并工具类,合并代码在afterCellDispose这个方法中,不管是列合并还是行合并其实是重写这个方法,将你的合并逻辑写在里面就可以

//列合并工具类
public class ExcelFillCellMergePrevColUtils implements CellWriteHandler {private static final String KEY ="%s-%s";//所有的合并信息都存在了这个map里面Map<String, Integer> mergeInfo = new HashMap<>();public ExcelFillCellMergePrevColUtils() {}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {//当前行int curRowIndex = cell.getRowIndex();//当前列int curColIndex = cell.getColumnIndex();Integer num = mergeInfo.get(String.format(KEY, curRowIndex, curColIndex));if(null != num){// 合并最后一行 ,列mergeWithPrevCol(writeSheetHolder, cell, curRowIndex, curColIndex,num);}}public void mergeWithPrevCol(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex, int num) {Sheet sheet = writeSheetHolder.getSheet();CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex, curRowIndex, curColIndex, curColIndex + num);sheet.addMergedRegion(cellRangeAddress);}//num从第几列开始增加多少列,(6,2,7)代表的意思就是第6行的第2列至第2+7也就是9列开始合并public void add (int curRowIndex,  int curColIndex , int num){mergeInfo.put(String.format(KEY, curRowIndex, curColIndex),num);}}

在这里插入图片描述
在这里插入图片描述
可以参考下面的这个excel看一下,广西壮族自治区的航站合计是从第8行,第2列开始+2列的范围合并

列合并效果图:
在这里插入图片描述
4.行合并
行合并工具类初级版本:

报错位置:ExcelFillCellMergeStrategyUtils合并策略类的 mergeWithPrevRow()方法中

这一行代码会报空指针异常 java.lang.NullPointerException

Row preRow = cell.getSheet().getRow(curRowIndex - 1);

在这里插入图片描述
原因:

debug发现,cell.getSheet() 行的下标第0到42的数据行,获取的是同一个 sheet 实例

当下标为43时,执行cell.getSheet()获取到的 sheet 实例不一样

而下标0到42的行数据被存储到 存储sheet中。如果上一行为空则去缓存中获取上一行,

writeSheetHolder.getCachedSheet()
 Row preRow = cell.getSheet().getRow(curRowIndex - 1);if (preRow == null) {// 当获取不到上一行数据时,使用缓存sheet中数据preRow = writeSheetHolder.getCachedSheet().getRow(curRowIndex - 1);}Cell preCell=preRow.getCell(curColIndex);

行合并工具类最终版:

public class ExcelFillCellMergeStrategyUtils implements CellWriteHandler {/*** 合并字段的下标*/private int[] mergeColumnIndex;/*** 合并几行*/private int mergeRowIndex;public ExcelFillCellMergeStrategyUtils(int mergeRowIndex, int[] mergeColumnIndex) {this.mergeRowIndex = mergeRowIndex;this.mergeColumnIndex = mergeColumnIndex;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,Head head, Integer integer, Integer integer1, Boolean aBoolean) {}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell,Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,CellData cellData, Cell cell, Head head, Integer integer, Boolean aBoolean) {}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {//当前行int curRowIndex = cell.getRowIndex();//当前列int curColIndex = cell.getColumnIndex();if (curRowIndex > mergeRowIndex) {for (int i = 0; i < mergeColumnIndex.length; i++) {if (curColIndex == mergeColumnIndex[i]) {mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);break;}}}}private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {//获取当前行的当前列的数据和上一行的当前列列数据,通过上一行数据是否相同进行合并Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() :cell.getNumericCellValue();Row preRow = cell.getSheet().getRow(curRowIndex - 1);if (preRow == null) {// 当获取不到上一行数据时,使用缓存sheet中数据preRow = writeSheetHolder.getCachedSheet().getRow(curRowIndex - 1);}Cell preCell=preRow.getCell(curColIndex);Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() :preCell.getNumericCellValue();// 比较当前行的第一列的单元格与上一行是否相同,相同合并当前单元格与上一行if (curData.equals(preData)) {Sheet sheet = writeSheetHolder.getSheet();List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();boolean isMerged = false;for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {CellRangeAddress cellRangeAddr = mergeRegions.get(i);// 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {sheet.removeMergedRegion(i);cellRangeAddr.setLastRow(curRowIndex);sheet.addMergedRegion(cellRangeAddr);isMerged = true;}}// 若上一个单元格未被合并,则新增合并单元if (!isMerged) {CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex,curColIndex);sheet.addMergedRegion(cellRangeAddress);}}}
}

5.excel字体样式内容居中
样式工具类:

public class CellStyleStrategy extends AbstractCellStyleStrategy {private WriteCellStyle headWriteCellStyle;private List<WriteCellStyle> contentWriteCellStyleList;private CellStyle headCellStyle;private List<CellStyle> contentCellStyleList;public CellStyleStrategy(WriteCellStyle headWriteCellStyle,List<WriteCellStyle> contentWriteCellStyleList) {this.headWriteCellStyle = headWriteCellStyle;this.contentWriteCellStyleList = contentWriteCellStyleList;}public CellStyleStrategy(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) {this.headWriteCellStyle = headWriteCellStyle;contentWriteCellStyleList = new ArrayList<WriteCellStyle>();contentWriteCellStyleList.add(contentWriteCellStyle);}@Overrideprotected void initCellStyle(Workbook workbook) {if (headWriteCellStyle != null) {headCellStyle = StyleUtil.buildHeadCellStyle(workbook, headWriteCellStyle);}if (contentWriteCellStyleList != null && !contentWriteCellStyleList.isEmpty()) {contentCellStyleList = new ArrayList<CellStyle>();for (WriteCellStyle writeCellStyle : contentWriteCellStyleList) {contentCellStyleList.add(StyleUtil.buildContentCellStyle(workbook, writeCellStyle));}}}@Overrideprotected void setHeadCellStyle(Cell cell, Head head, Integer relativeRowIndex) {if (headCellStyle == null) {return;}cell.setCellStyle(headCellStyle);}@Overrideprotected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) {if (contentCellStyleList == null || contentCellStyleList.isEmpty()) {return;}cell.setCellStyle(contentCellStyleList.get(0));}}
 public CellStyleStrategy horizontalCellStyleStrategyBuilder() {WriteCellStyle headWriteCellStyle = new WriteCellStyle();//设置头字体WriteFont headWriteFont = new WriteFont();headWriteFont.setFontHeightInPoints((short) 13);headWriteFont.setBold(true);headWriteCellStyle.setWriteFont(headWriteFont);//设置头居中headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);//内容策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();//设置 水平居中contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);//垂直居中contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);return new CellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);}

6.将三个工具类初始化后注册后最终代码:

 public void exportExcel(HttpServletResponse response, String beginDate, String endDate) throws IOException {ExcelFillCellMergePrevColUtils excelFillCellMergePrevColUtils = new ExcelFillCellMergePrevColUtils();String terminalTotal = "航站合计";String provinceTotal = "省区合计";Map<String, ProvinceInfo> map = handlePlaneDownFire(beginDate, endDate);List<PlaneDownFire> list = new ArrayList<>();map.forEach((k, v) -> {//添加航站合计v.getTerminalInfos().forEach((k1, v1) -> {list.addAll(v1.getList());int size = list.size();excelFillCellMergePrevColUtils.add(size + 3, 2, 2);CommissionInfo terminalCommissionInfoTotal = v1.getSum();PlaneDownFire planeDownFire = CommissionInfoConvert.INSTANCE.commissionInfo2planeDownFire(terminalCommissionInfoTotal);planeDownFire.setProvincialArea(v.getName()).setMachineNumber(String.valueOf(size)).setFlyingCommission(String.valueOf(size)).setTerminal(v1.getName()).setModel(terminalTotal);list.add(planeDownFire);});int size = list.size();excelFillCellMergePrevColUtils.add(size + 3, 1, 3);//省区合计CommissionInfo provinceCommissionInfoTotal = v.getSum();PlaneDownFire planeDownFire = CommissionInfoConvert.INSTANCE.commissionInfo2planeDownFire(provinceCommissionInfoTotal);planeDownFire.setProvincialArea(v.getName()).setTerminal(provinceTotal).setModel(String.valueOf(size));list.add(planeDownFire);});//设置第几列开始合并int[] mergeColumnIndex = {0, 1, 2, 3};//设置第几行开始合并int mergeRowIndex = 3;ExcelFillCellMergeStrategyUtils excelFillCellMergeStrategyUtils = new ExcelFillCellMergeStrategyUtils(mergeRowIndex, mergeColumnIndex);InputStream template = new PathMatchingResourcePatternResolver().getResource("templates/飞机扑救火场统计表.xlsx").getInputStream();response.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码response.setHeader("Content-Disposition","attachment;filename=" + java.net.URLEncoder.encode("飞机扑救火场统计表.xlsx", "UTF-8"));//ExcelWriter该对象用于通过POI将值写入ExcelExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(template)//样式注册.registerWriteHandler(horizontalCellStyleStrategyBuilder())//行注册.registerWriteHandler(excelFillCellMergeStrategyUtils)//列注册.registerWriteHandler(excelFillCellMergePrevColUtils).build();//构建excel的sheetWriteSheet writeSheet = EasyExcel.writerSheet().build();Map<String, String> fileData = new HashMap<>();fileData.put("beginDate", beginDate);fileData.put("endDate", endDate);excelWriter.fill(fileData, writeSheet);excelWriter.fill(list, writeSheet);excelWriter.finish();}

总结:EasyExcel动态导出几乎能够满足大部分需求,说到底还是实现CellWriteHandler 类里面的

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

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

相关文章

系统分析师(软考)知识点整理(一)

第一章 信息 信息是不确定性的减少 xi: n个状态中的第i个状态p(xi):出现第i个状态的概率b: b一般取值为2 特征 #mermaid-svg-pvPkY9RE5GZIIIxl {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-pvPkY9RE5GZIIIxl…

一篇文章带你了解各个程序员接单平台,让你选择不再迷茫!!!

相信现在很多程序员都已经走上了或者准备走上网上接单这条路&#xff0c;但是目前市面上的接单平台可谓五花八门&#xff0c;对于各个平台的优缺点&#xff0c;不同的程序员该如何选择适合自己的接单平台&#xff0c;你又是否了解呢&#xff1f; 接下来就让小编用一篇文章来为…

Gemini自曝中文用百度文心一言训练,网友看呆:大公司互薅羊毛??

谷歌Gemini中文语料疑似来自文心一言&#xff1f;&#xff1f;&#xff1f; 先是有读者向我们爆料&#xff1a; 在谷歌Vertex AI平台使用该模型进行中文对话时&#xff0c;Gemini-Pro直接表示自己是百度语言大模型。 很快&#xff0c;有微博大V阑夕夜也发博称&#xff1a; 在…

超实用的Web兼容性测试经验总结,建议Mark

在日常工作中&#xff0c;我们经常碰到网页不兼容的问题。我们之所以要做兼容性测试&#xff0c;目的在于保证待测试项目在不同的操作系统平台上正常运行。 主要包括待测试项目能在同一操作系统平台的不同版本上正常运行&#xff1b;待测试项目能与相关的其他软件或系统的“和…

手撕HashMap源码2

目录 引言 putTreeVal红黑树添加结点方法讲解 treeifyBin进行树化的方法&#xff08;虚假的树化&#xff09; treeify真正的树化操作 从扩容的部分来分析红黑树的代码 split红黑树扩容迁移的方法 untreeify链化&#xff08;退树成链&#xff09; 红黑树代码分析 rota…

FO with Prefix Hashing KEM Generalizations

参考文献&#xff1a; [Has88] Hastad J. Solving simultaneous modular equations of low degree[J]. siam Journal on Computing, 1988, 17(2): 336-341.[BBM00] Bellare M, Boldyreva A, Micali S. Public-key encryption in a multi-user setting: Security proofs and im…

2023 英特尔On技术创新大会直播 | AI 融合发展之旅

前言 2023 年的英特尔 On 技术创新大会中国站&#xff0c;主要聚焦最新一代增强 AI 能力的计算平台&#xff0c;深度讲解如何支持开放、多架构的软件方案&#xff0c;以赋能人工智能并推动其持续发展。 大会的目标之一是优化系统并赋能开发者&#xff0c;特别注重芯片增强技术…

国产划片机品牌众多,如何选择优质的供应商?

在半导体行业的发展浪潮中&#xff0c;划片机作为关键设备之一&#xff0c;其性能和质量对于生产过程的高效性和产品的质量具有至关重要的影响。近年来&#xff0c;国产划片机的品牌数量不断增多&#xff0c;为半导体行业提供了更多的选择。然而&#xff0c;如何从众多的品牌中…

【Python炫酷系列】一闪一闪亮星星,漫天都是小星星(完整代码)

文章目录 环境需求完整代码详细分析系列文章环境需求 python3.11.4及以上版本PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.…

Python 爬虫之简单的爬虫(四)

爬取动态网页&#xff08;下&#xff09; 文章目录 爬取动态网页&#xff08;下&#xff09;前言一、大致内容二、基本思路三、代码编写1.引入库2.加载网页数据3.获取并保存4.保存文档 总结 前言 上篇主要讲了如何去爬取数据&#xff0c;这篇来讲一下如何在获取的同时将数据整…

每个开发人员都应该知道的六个生成式 AI 框架和工具

在快速发展的技术环境中&#xff0c;生成式人工智能是一股革命性的力量&#xff0c;它改变了开发人员处理复杂问题和创新的方式。本文深入探讨了生成式 AI 的世界&#xff0c;揭示了对每个开发人员都至关重要的框架和工具。 1. LangChain LangChain 由 Harrison Chase 开发并于…

Ansible自动化运维以及模块使用

ansible的作用&#xff1a; 远程操作主机功能 自动化运维(playbook剧本基于yaml格式书写) ansible是基于python开发的配置管理和应用部署工具。在自动化运维中&#xff0c;现在是异军突起 ansible能够批量配置、部署、管理上千台主机。类似于Xshell的一键输入工具。不需要每…

通过层进行高效学习:探索深度神经网络中的层次稀疏表示

一、介绍 深度学习中的层次稀疏表示是人工智能领域日益重要的研究领域。本文将探讨分层稀疏表示的概念、它们在深度学习中的意义、应用、挑战和未来方向。 最大限度地提高人工智能的效率和性能&#xff1a;深度学习系统中分层稀疏表示的力量。 二、理解层次稀疏表示 分层稀疏表…

JDK各个版本特性讲解-JDK19特性

JDK各个版本特性讲解-JDK19特性 一、JAVA19概述二、新特性介绍1. 记录模式(预览版本)2.Linux/RISC-V 移植3.外部函数和内存 API &#xff08;预览版&#xff09;4.虚拟线程(预览版)5.Vector API &#xff08;第四次孵化&#xff09;6.Switch 模式匹配&#xff08;第三预览版&am…

从C代码制作chm开发文档【doxygen + graphviz+winChm】

需要的工具&#xff1a; 1. doxygen 最新版本 2. graphviz 最新版本 3. winChm破解版本 1. 最后制作的效果 2. 生成HTML文档 生成hmtl文档是勾选如下2项&#xff0c;为生成chm准备&#xff1a; 需要选择如下2项&#xff1a; generate HTMLHELP 生…

C语言数据结构-排序

文章目录 1 排序的概念及运用1.1 排序的概念1.2 排序的应用 2 插入排序2.1 直接插入排序2.2 希尔排序2.3 直接排序和希尔排序对比 3 选择排序3.1 堆排序3.2 直接选择排序 4 交换排序4.1 冒泡排序4.2 快速排序4.2.1 挖坑法14.2.2 挖坑法24.2.3 挖坑法3 5 并归排序6 十万级别数据…

深入探索Git的高级技巧与神奇操作(分支,高效合并)

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 深入探索Git的高级技巧与神奇操作 前言强制推送的妙用1. 什么是强制推送&#xff1f;2. 为什么需要使用强制推送&#xff1f;3. 强制推送的风险与注意事项4. 如何正确、安全地执行强制推送步骤&#x…

vCenter HA拆分和部署

原创作者&#xff1a;运维工程师 谢晋 vCenter HA拆分和部署 拆分vCenter HA部署vCenter HA 拆分vCenter HA 客户vCenter HA内一台虚拟机出现故障无法连接&#xff0c;报错如下&#xff1a; 点击移除集群报错如下&#xff1a; 查找官方KB&#xff0c;按照官方KB进行移除…

PyCharm关闭项目很慢

我的版本&#xff1a; PyCharm 2023.2.5 (Professional Edition) 问题&#xff1a; 关闭项目的时候显示一直在关闭项目 &#xff08;单次解决&#xff1a;任务管理器里面杀掉PyCharm&#xff09; 解决方案&#xff1a; 在PyCharm中按下快捷键 CtrlShiftA。 输入Registry或…

如何同时给每张PPT插入不同的图片?这2种方法可行!

有时候创作PPT&#xff0c;我们需要把几十张图片插入到PowerPoint中&#xff0c;每张图片作为一张幻灯片&#xff0c;如果一张张手动操作&#xff0c;那就未免太花时间了。今天小编来分享2种方法&#xff0c;可以让您快速给每张PPT插入不同图片。 方法一、使用“创建相册” 1.…