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

2023-12-19 20:44

本文主要是介绍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 类里面的

这篇关于EasyExcel模板导出(行和列自动合并)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/513645

相关文章

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

IDEA如何让控制台自动换行

《IDEA如何让控制台自动换行》本文介绍了如何在IDEA中设置控制台自动换行,具体步骤为:File-Settings-Editor-General-Console,然后勾选Usesoftwrapsin... 目录IDEA如何让控制台自http://www.chinasem.cn动换行操作流http://www

vscode保存代码时自动eslint格式化图文教程

《vscode保存代码时自动eslint格式化图文教程》:本文主要介绍vscode保存代码时自动eslint格式化的相关资料,包括打开设置文件并复制特定内容,文中通过代码介绍的非常详细,需要的朋友... 目录1、点击设置2、选择远程--->点击右上角打开设置3、会弹出settings.json文件,将以下内

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

详解Vue如何使用xlsx库导出Excel文件

《详解Vue如何使用xlsx库导出Excel文件》第三方库xlsx提供了强大的功能来处理Excel文件,它可以简化导出Excel文件这个过程,本文将为大家详细介绍一下它的具体使用,需要的小伙伴可以了解... 目录1. 安装依赖2. 创建vue组件3. 解释代码在Vue.js项目中导出Excel文件,使用第三

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

基于C#实现PDF文件合并工具

《基于C#实现PDF文件合并工具》这篇文章主要为大家详细介绍了如何基于C#实现一个简单的PDF文件合并工具,文中的示例代码简洁易懂,有需要的小伙伴可以跟随小编一起学习一下... 界面主要用于发票PDF文件的合并。经常出差要报销的很有用。代码using System;using System.Col

Python实现将实体类列表数据导出到Excel文件

《Python实现将实体类列表数据导出到Excel文件》在数据处理和报告生成中,将实体类的列表数据导出到Excel文件是一项常见任务,Python提供了多种库来实现这一目标,下面就来跟随小编一起学习一... 目录一、环境准备二、定义实体类三、创建实体类列表四、将实体类列表转换为DataFrame五、导出Da