easyExcle单元格合并

自定义单元格合并策略:

/*** 自定义单元格合并策略** @create: 2023-11-15 13:41**/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Slf4j
public class EasyExcelCustomMergeStrategy implements RowWriteHandler {/*** 总数*/private Integer totalNum;//合并行计数private int count;//要合并的列 从0开始private int[] mergeColumnIndex;//已合并单元数private int mergedTotalCount = 0;public EasyExcelCustomMergeStrategy(int[] mergeColumnIndex) {this.mergeColumnIndex = mergeColumnIndex;}// 每写入一行会执行一次afterRowDispose,存在合并行时进行合并方法mergeSameRow,否则什么也不做@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {//当前行索引int curRowNum = row.getRowNum();if (mergeColumnIndex != null && mergeColumnIndex.length > 0 && !isHead) {//当前行第一列单元格Cell curA1Cell = row.getCell(0);Object curA1Data = curA1Cell.getCellTypeEnum() == CellType.STRING ? curA1Cell.getStringCellValue() : curA1Cell.getNumericCellValue();//上一行第一列单元格Cell preA1Cell = row.getSheet().getRow(curRowNum - 1).getCell(0);Object preA1Data = preA1Cell.getCellTypeEnum() == CellType.STRING ? preA1Cell.getStringCellValue() : preA1Cell.getNumericCellValue();if (curA1Data.equals(preA1Data)) {count++;} else {if (count > 0) {for (int columnIndex : mergeColumnIndex) {mergeSameRow(writeSheetHolder, curRowNum, count, columnIndex);}//for (int i = 0; i < mergeColumnIndex.length; i++) {////}count = 0;}}// 最后一行存在合并时,需要单独调用合并,否则不会执行mergeSameRowif (curRowNum == totalNum && count > 0) {for (int columnIndex : mergeColumnIndex) {mergeSameRow(writeSheetHolder, curRowNum + 1, count, columnIndex);}}}}/*** 按列合并单元格** @param writeSheetHolder 写出处理* @param curRowIndex      当前行索引,有n行固定行就加n* @param needMergeNum     需要合并的行* @param curColIndex      需要合并的列*/private void mergeSameRow(WriteSheetHolder writeSheetHolder, int curRowIndex, int needMergeNum, int curColIndex) {Sheet sheet = writeSheetHolder.getSheet();try {Field sh = sheet.getClass().getDeclaredField("_sh");sh.setAccessible(true);XSSFSheet shSheet = (XSSFSheet) sh.get(sheet);CTWorksheet worksheet = shSheet.getCTWorksheet();CTMergeCells ctMergeCells = mergedTotalCount > 0 ? worksheet.getMergeCells() : worksheet.addNewMergeCells();CTMergeCell ctMergeCell = ctMergeCells.addNewMergeCell();CellRangeAddress cellAddresses = new CellRangeAddress(curRowIndex - needMergeNum - 1, curRowIndex - 1, curColIndex, curColIndex);ctMergeCell.setRef(cellAddresses.formatAsString());mergedTotalCount++;} catch (Exception e) {log.error("导出出错!", e);throw new BusinessException("导出出错!\n");}}
}

easyExcel提供了两种合并单元格方法:addMergedRegion和addMergedRegionUnsafe,在使用中发现会遇到不同的问题:

如果将需要合并的数据一次性传入afterRowDispose方法进行合并,在合并过程中使用addMergedRegionUnsafe方法合并单元格可能会导致工作簿损坏,而使用addMergedRegion会进行单元格是否重复合并的校验,会直接抛出异常。

使用addMergedRegionUnsafe方法合并单元格导致的问题:

使用addMergedRegion进行的校验:

/*** adds a merged region of cells (hence those cells form one)** @param region (rowfrom/colfrom-rowto/colto) to merge* @param validate whether to validate merged region* @return index of this region* @throws IllegalArgumentException if region contains fewer than 2 cells* @throws IllegalStateException if region intersects with an existing merged region* or multi-cell array formula on this sheet*/
private int addMergedRegion(CellRangeAddress region, boolean validate) {if (region.getNumberOfCells() < 2) {// 区域包含少于 2 个单元格throw new IllegalArgumentException("Merged region " + region.formatAsString() + " must contain 2 or more cells");}region.validate(SpreadsheetVersion.EXCEL97);if (validate) {// throw IllegalStateException if the argument CellRangeAddress intersects with// a multi-cell array formula defined in this sheet// 此工作表上的多单元格数组公式validateArrayFormulas(region);// Throw IllegalStateException if the argument CellRangeAddress intersects with// a merged region already in this sheet// 区域与现有合并区域相交validateMergedRegions(region);}return _sheet.addMergedRegion(region.getFirstRow(),region.getFirstColumn(),region.getLastRow(),region.getLastColumn());
}

导致错误产生的代码:

public class EasyExcelCustomMergeStrategy implements RowWriteHandler {//合并坐标集合private List<CellRangeAddress> cellRangeAddress;//从哪行开始private int beginRow;//合并行数private int mergeRows;public PiFillMergeStrategy(List<CellRangeAddress> cellRangeAddress, int beginRow, int mergeRows) {this.cellRangeAddress = cellRangeAddress;this.beginRow = beginRow;this.mergeRows = mergeRows;}@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {if (CollectionUtil.isNotEmpty(cellRangeAddress)) {if (row.getRowNum() >= beginRow && row.getRowNum() <= beginRow + mergeRows) {for (CellRangeAddress item : cellRangeAddress) {writeSheetHolder.getSheet().addMergedRegionUnsafe(item);}}}}}

因此合并时循环每一条数据判断是否与上一条数据是否相同,相同时再逐条进行合并,写法使用XSSFSheet写法或使用easyExcel自带的两种合并方法:

private void mergeSameRow(WriteSheetHolder writeSheetHolder, int curRowIndex, int needMergeNum, int curColIndex) {Sheet sheet = writeSheetHolder.getSheet();CellRangeAddress cellAddresses = new CellRangeAddress(curRowIndex - needMergeNum - 1, curRowIndex - 1, curColIndex, curColIndex);sheet.addMergedRegionUnsafe(cellAddresses);// 或使用// sheet.addMergedRegion(cellAddresses);
}

合并策略的使用:

public void download(HttpServletResponse response) {/***  此处为业务数据****/String subject = "XXX";String fileName = subject.concat(".xlsx");//定义多级表头List<List<String>> headList = new ArrayList<>();//定义数据体List<List<Object>> dataList = new ArrayList<>();this.getHeadAndData(headList, dataList);// 定义合并单元格 (根据业务需求自定义)List<Integer> mergeColumnIndex = new ArrayList<>();int mergeCol = headList.size() - offerVOList.size() - 3;for (int i = 0; i < mergeCol; i++) {mergeColumnIndex.add(i);}for (int j = mergeCol + 3; j < headList.size(); j++) {mergeColumnIndex.add(j);}// 生成自定义策略EasyExcelCustomMergeStrategy customMergeStrategy = new EasyExcelCustomMergeStrategy(mergeColumnIndex.stream().filter(Objects::nonNull).mapToInt(i->i).toArray());customMergeStrategy.setTotalNum(dataList.size());try {response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));EasyExcel.write(response.getOutputStream()).excelType(ExcelTypeEnum.XLSX).head(headList).sheet(subject).registerWriteHandler(customMergeStrategy)//调用策略.doWrite(dataList);} catch (Exception e) {throw new Exception(String.format("生成文件(%s)失败", fileName), e);}
}

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

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

相关文章

Power Automate-创建自定义连接器

点击左侧导航栏&#xff0c;更多&#xff0c;点击全部发现 点击下方的自定义连接器 点击从空白创建 注意命名要用英文 常规信息中可以上传连接器icon、写一些说明 方案是观察接口地址前面的文本&#xff0c;主机是下方接口地址中蓝色框中的内容 点击下一步&#xff0c;根据API自…

kotlin--2.面向对象

目录 一.概念 Kotlin 类/对象 二.创建类 1.创建类 2.构造函数 2.getter 和 setter 实例 3.主构造器 实例 4.次构造函数 实例 5.抽象类 6.嵌套类 7.内部类 8.匿名内部类 9.类的修饰符 实例 三.继承 1.概念 2.构造函数 (1)子类有主构造函数 (2)子类没有主构…

gdb详解【Linux知识贩卖机】

你背朝太阳&#xff0c;就只能看到自己的影子。 --纪伯伦语录 文章目录 简介准备常用命令查看代码&#xff08;list&#xff09;运行&#xff08;run&#xff09;打断点&#xff08;break&#xff09;逐语句&#xff08;step&#xff09;逐过程&#xff08;next&#xff09;完成…

C# 使用Microsoft.Office.Interop.Excel库操作Excel

1.在NuGet管理包中搜索&#xff1a;Microsoft.Office.Interop.Excel&#xff0c;如下图红色标记处所示&#xff0c;进行安装 2. 安装完成后&#xff0c;在程序中引入命名空间如下所示&#xff1a; using Microsoft.Office.Interop.Excel; //第一步 添加excel第三方库 usi…

人格障碍在线测试,人格障碍筛查和判断 PDQ-4+

每个人都是独一无二的&#xff0c;每个人都存在人格上的偏差&#xff0c;日常生活中我们携带着自己的人格在忙碌&#xff0c;在不够成对学习、生活和工作的影响下&#xff0c;我们认为都是健康的人格&#xff0c;反之则属于人格障碍。 人格障碍给我们的日常生活带来极大的影响…

打开PDF文件之后不能编辑,有哪些原因?

打开PDF文件之后发现没有办法编辑PDF文件&#xff0c;都有哪些原因呢&#xff1f; 首先我们可以考虑一下&#xff0c;PDF文件中的内容是否是图片&#xff0c;如果确认是图片文件&#xff0c;那么我们想要编辑&#xff0c;就可以先使用PDF编辑器中的OCR扫描功能&#xff0c;将图…

spring boot 环境变量问题

org.yaml.snakeyaml.scanner.ScannerException: while scanning for the next token found character that cannot start any token. (Do not use for indentation) in reader, line 4, column 13: active: spring.profiles.active 添加 以下依赖即可 <!-- 解决环…

如何通过 wireshark 捕获 C# 上传的图片

一&#xff1a;背景 1. 讲故事 这些天计划好好研究下tcp/ip&#xff0c;以及socket套接字&#xff0c;毕竟工控中设计到各种交互协议&#xff0c;如果只是模模糊糊的了解&#xff0c;对分析此类dump还是非常不利的&#xff0c;而研究协议最好的入手点就是用抓包工具 wireshar…

一加手机全球摄影展深圳开展 历年获奖作品齐登场

11 月 18 日至 12 月 3 日&#xff0c;一加手机将携手国际摄影奖&#xff08;International Photography Awards&#xff0c;以下简称IPA&#xff09;&#xff0c;在深圳市南山区海岸城购物中心举办一加手机全球摄影展&#xff08;OnePlus Global Photography Exhibition&#…

npm源管理工具nrm

要设置npm使用国内源&#xff0c;可以按照以下步骤进行操作&#xff1a; 安装nrm&#xff08;可选&#xff09;&#xff1a;nrm是一个用于管理npm源的工具&#xff0c;可以方便地切换不同的源。你可以使用以下命令全局安装nrm&#xff1a; npm install -g nrm查看可用的源&am…

容联云发布生成式应用,让每个企业都拥有大模型沟通能力

基于容联云自主研发的赤兔大模型能力&#xff0c;容联云容犀机器人真正将大模型强大的理解能力、知识学习能力、总结能力、挖掘能力、推理能力融入于实际落地应用中。 开创性的打造生成式场景化智能问答、生成式智能辅助、AI运营话术库&#xff0c;帮助企业洞悉更精准的客户真…

Ubuntu 22.04 LTS ffmpeg mp4 gif 添加图片水印

ffmpeg编译安装6.0.1&#xff0c;参考 Ubuntu 20.04 LTS ffmpeg gif mp4 互转 许编译安装ffmpeg &#xff1b;解决gif转mp4转换后无法播放问题-CSDN博客 准备一个logo MP4添加水印 ffmpeg -i 2.mp4 -vf "movielogo.png[watermark];[in][watermark]overlayx10:y10[out]&…

分类预测 | MATLAB实现基于Isomap降维算法与改进蜜獾算法IHBA的Adaboost-SVM集成多输入分类预测

分类预测 | MATLAB实现基于Isomap降维算法与改进蜜獾算法IHBA的Adaboost-SVM集成多输入分类预测 目录 分类预测 | MATLAB实现基于Isomap降维算法与改进蜜獾算法IHBA的Adaboost-SVM集成多输入分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 Isomap-Adaboost-IHBA-…

指针与函数

指针函数&#xff1a;函数的返回值可以是整型值、浮点型值、字符型值等&#xff0c;在C语言中还允许一个函数的返回值是一个指针&#xff08;地址&#xff09;&#xff0c;这种返回指针的函数称为指针函数。 指针函数语法格式&#xff1a; 基类型 * 函数名&#xff08;参数列…

Docker存储驱动之- overlay2

docker支持多种graphDriver&#xff0c;包括vfs、devicemapper、overlay、overlay2、aufs等等&#xff0c;其中最常用的就是aufs了&#xff0c;但随着linux内核3.18把overlay纳入其中&#xff0c;overlay的地位变得更重&#xff0c;最近也在自己的虚拟机上用overlay2作为docker…

基于Vue+SpringBoot的城市桥梁道路管理系统 开源项目

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 查询城市桥梁4.2 新增城市桥梁4.3 编辑城市桥梁4.4 删除城市桥梁4.5 查询单个城市桥梁 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的城市桥梁道路管理系统&#xff0c;支持…

redis相关文章汇总

一、redis结构 1-redis-功能分类 跳转 redis数据结构对比跳转 以下链接忽略&#xff1a; 01-Redis数据结构-汇总跳转 02-Redis数据结构-List跳转 03-Redis数据结构-dict跳转 二、redis问题攻克难点 缓存穿透、缓存雪崩、缓存击穿区别和解决方案跳转 DB和缓存一致性的问题…

flutter pod install, Error installing FMDB

这是我的错误提示 [!] Error installing FMDB [!] /usr/bin/git clone https://github.com/ccgus/fmdb.git /var/folders/gs/npkkfph92xndgh137z132qf00000gn/T/d20231113-95745-x4208e --template --single-branch --depth 1 --branch 2.7.5Cloning into /var/folders/gs/npk…

本地化小程序运营 同城小程序开发

时空的限制让本地化的线上平台成为一种追求&#xff0c;58及某团正式深挖人们城镇化、本地化的信息和商业需求而崛起的平台&#xff0c;将二者结合成本地化小程序&#xff0c;显然有着巨大的市场机会。本地化小程序运营可以结合本地化生活需求的一些信息&#xff0c;以及激发商…