使用 EasyExcel 动态添加自增序号列
前言
本文将介绍如何通过使用EasyExcel
自定义拦截器实现在最终的Excel
文件中新增一列自增的序号列,最终的效果如下:
此外,本文所使用的完整代码示例已上传到GitHub。
实现
本文主要是通过自定义一个继承AbstractRowWriteHandler
的拦截器来实现在最终导出的结果中新增序号列,通过修改源码中保存头部标题的Map
内容来给自己添加的序号列留出位置,先展示最终的代码:
/*** 自定义 excel 行处理器, 增加序号列** @author butterfly* @date 2020-09-05*/
@Component
public class AddNoHandler extends AbstractRowWriteHandler {private boolean init = true;@Overridepublic void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) {if (init) {// 修改存储头部及对应字段信息的 map, 将其中的内容均右移一位, 给新增的序列号预留为第一列ExcelWriteHeadProperty excelWriteHeadProperty = writeSheetHolder.excelWriteHeadProperty();Map<Integer, Head> headMap = excelWriteHeadProperty.getHeadMap();Map<Integer, ExcelContentProperty> contentMap = excelWriteHeadProperty.getContentPropertyMap();int size = headMap.size();for (int current = size; current > 0; current--) {int previous = current - 1;headMap.put(current, headMap.get(previous));contentMap.put(current, contentMap.get(previous));}// 空出第一列headMap.remove(0);contentMap.remove(0);// 只需要修改一次 map 即可, 故使用 init 变量进行控制init = false;}}@Overridepublic void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,Integer relativeRowIndex, Boolean isHead) {// 在行创建完成后添加序号列Cell cell = row.createCell(0);int rowNum = row.getRowNum();if (rowNum == 0) {cell.setCellValue(ExcelConstant.TITLE);} else {cell.setCellValue(rowNum);}}@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,Integer relativeRowIndex, Boolean isHead) {if (row.getLastCellNum() > 1) {// 将自定义新增的序号列的样式设置与默认的样式一致row.getCell(0).setCellStyle(row.getCell(1).getCellStyle());}}}
afterRowCreate
和afterRowDispose
方法中的内容很好理解,一个用于控制控制新增序号列的内容,一个用于控制新增列的样式。而beforeRowCreate
中的内容则稍微复杂,主要用于给新增的序号列空出位置。同时,由于beforeRowCreate
会在每次创建行时均会被调用,但是原始的存储头部信息的Map
只需要修改一次,因此这里通过使用init
变量控制只会修改一次。
思路
通过查看com.alibaba.excel.write.executor.ExcelWriteAddExecutor
类中的addOneRowOfDataToExcel
方法源码,可以看到在新增行的前后会分别调用beforeRowCreate
和afterRowCreate
方法,并且在一行数据添加完成后会调用afterRowDispose
,这也是上述拦截器生效的原理,源码如下:
private void addOneRowOfDataToExcel(Object oneRowData, int n, int relativeRowIndex,Map<Integer, Field> sortedAllFiledMap) {// 行数据为空, 直接返回if (oneRowData == null) {return;}// 创建数据行对象, 同时分别在创建行前后调用拦截器WriteHandlerUtils.beforeRowCreate(writeContext, n, relativeRowIndex, Boolean.FALSE);Row row = WorkBookUtil.createRow(writeContext.writeSheetHolder().getSheet(), n);WriteHandlerUtils.afterRowCreate(writeContext, row, relativeRowIndex, Boolean.FALSE);// 将实体数据内容填充到行中if (oneRowData instanceof List) {addBasicTypeToExcel((List) oneRowData, row, relativeRowIndex);} else {// 下面会继续查看这个方法addJavaObjectToExcel(oneRowData, row, relativeRowIndex, sortedAllFiledMap);}// 行创建完成后, 调用相应拦截器WriteHandlerUtils.afterRowDispose(writeContext, row, relativeRowIndex, Boolean.FALSE);
}
而之所以我们修改headMap
和contentMap
的内容就可以实现最终效果,只需要继续查看该类中addJavaObjectToExcel
方法的代码即可知道原因:
private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex,Map<Integer, Field> sortedAllFiledMap) {WriteHolder currentWriteHolder = writeContext.currentWriteHolder();// 将自己的实体数据映射到 beanMapBeanMap beanMap = BeanMap.create(oneRowData);Set<String> beanMapHandledSet = new HashSet<String>();int cellIndex = 0;// If it's a class it needs to be cast by typeif (HeadKindEnum.CLASS.equals(writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadKind())) {// 我们修改的就是这里的 headMap 和 contentPropertyMap 内容Map<Integer, Head> headMap = writeContext.currentWriteHolder().excelWriteHeadProperty().getHeadMap();Map<Integer, ExcelContentProperty> contentPropertyMap =writeContext.currentWriteHolder().excelWriteHeadProperty().getContentPropertyMap();// 遍历所有的列头, 插入数据for (Map.Entry<Integer, ExcelContentProperty> entry : contentPropertyMap.entrySet()) {// 获取 cell 的下标, 后续将内容插入指定的列cellIndex = entry.getKey();ExcelContentProperty excelContentProperty = entry.getValue();String name = excelContentProperty.getField().getName();if (!beanMap.containsKey(name)) {continue;}// 控制单元格的内容Head head = headMap.get(cellIndex);WriteHandlerUtils.beforeCellCreate(writeContext, row, head, cellIndex, relativeRowIndex, Boolean.FALSE);Cell cell = WorkBookUtil.createCell(row, cellIndex);WriteHandlerUtils.afterCellCreate(writeContext, cell, head, relativeRowIndex, Boolean.FALSE);Object value = beanMap.get(name);CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, value, excelContentProperty, head, relativeRowIndex);WriteHandlerUtils.afterCellDispose(writeContext, cellData, cell, head, relativeRowIndex, Boolean.FALSE);beanMapHandledSet.add(name);}}// 省略了后面无关的内容}
其它
通过以上自定义的拦截器,就可以写一个简单的demo
进行测试:
/*** Excel 下载控制器** @author butterfly* @date 2021-09-05*/
@RestController
public class ExcelController {/*** 添加序号列测试** @param response response*/@GetMapping("/col")public void col(HttpServletResponse response) {try {List<Student> students = getStudentList();EasyExcel.write(response.getOutputStream(), Student.class).registerWriteHandler(new AddNoHandler()).sheet().doWrite(students);} catch (Exception e) {System.out.println(ExcelConstant.DOWNLOAD_FAILED);}}/*** 生成学生列表** @return 学生列表*/private List<Student> getStudentList() {return Arrays.asList(new Student("2021090101", "张三", 19),new Student("2021090102", "李四", 18),new Student("2021090103", "王二", 20));}
}
然后是前端的测试代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>文件下载测试</title><script src="https://cdn.staticfile.org/axios/0.21.2/axios.min.js"></script>
</head>
<body><button onclick="col()">添加序号列测试</button><script>function col() {download('http://localhost:8080/col', 'col.xlsx')}function download(url, name) {axios({url: url,responseType: 'blob'}).then((response) => {const URL = window.URL.createObjectURL(response.data)const tempLink = document.createElement('a')tempLink.style.display = 'none'tempLink.href = URLtempLink.setAttribute('download', name)if (typeof tempLink.download === 'undefined') {tempLink.setAttribute('target', '_blank')}document.body.appendChild(tempLink)tempLink.click()document.body.removeChild(tempLink)window.URL.revokeObjectURL(URL)})}</script>
</body>
</html>
总结
以上便是实现动态添加自增序号列的一种思路,除此之外还可以通过在实体中添加序号
这一个字段,然后修改列表的数据,或者只需要在afterRowCreate
中设置序号数据即可,而不需要再修改头部Map
的数据。还有一种通过自定义模板,然后通过填充模板来实现同样的效果,可以参考https://www.yuque.com/easyexcel/doc/fill
,但是这两种方法都需要对原有数据有所修改,或者需要增加定义模板的操作,这里不再介绍。
使用 EasyExcel 动态添加自增序号列相关推荐
- sql 对查询出的 结果集 添加 自增序号列/排序列
目录 今天做了一个关于排行榜的功能 参考一: 第一种方法: 第二种方法: 参考二: 对于 变量 i 的分析: 参考三(很全面易懂): 一.变量的定义及使用 二.变量的赋值 三.变量使用小技巧 参 ...
- html中列表前面的序号带圆圈,js动态添加带圆圈序号列表方法
js动态添加带圆圈序号列表方法 发布时间:2021-02-19 11:30:34 来源:亿速云 阅读:69 作者:小新 这篇文章给大家分享的是有关js动态添加带圆圈序号列表方法的内容.小编觉得挺实用的 ...
- antd table合并行或者列(动态添加合并行、列)
antd table合并行或者列(动态添加合并行.列) 表头只支持列合并,使用 column 里的 colSpan 进行设置. 表格支持行/列合并,使用 render 里的单元格属性 colSpan ...
- html中列表前面的序号带圆圈,js动态添加带圆圈序号列表的实例代码
1.先在body里面添加ul标签 2.通过js获取到id等于list的标签 定义一个空字符串用来连接增加的标签,并展示出来 如图的js代码展示的是前三个颜色不同,余下的颜色相同的圆圈序号 functi ...
- 如何在Hive、MySQL、Oracle中分别添加自增序号
最近公司业务涉及到了在相应库中添加自增序号这种操作,闲暇之余,整理如下,仅供参考~ 一.Hive 1.首先在Hive中建立一个测试表 create table xzw(id int, name str ...
- iview给table组件添加自增序号
傻逼了,iview自带了自增序号,还是记录一下吧 template: <Table:columns="informationTable":data="informa ...
- EasyExcel动态跨行合并和自定义列宽的策略
1. 动态表头 2.自定义跨行合并的策略 3.自定义列宽的策略 下载表格的代码 new 出合并单元格的对象 第一个参数写入需要合并的列的字符串列表,会根据相同自动合并 第二个参数写入要合并单元格是第几 ...
- MySQL中如何为查询的数据添加自增序号、顺序呢
背景介绍 很多时候我们在使用mysql查询数据的时候都会遇到一个问题,就是查询出来了一堆数据,但是查询的数据的表并没有序号,然而部分数据库显示工具是有外带序号显示,但是这种序号不是由sql产生的,而是 ...
- js动态添加带圆圈序号列表
1.先在body里面添加ul标签 <!-- 无序列表 --> <ul id="list"></ul> 2.通过js获取到id等于list的标签 ...
最新文章
- Qmail的介绍和配置
- 6月Top 20榜单出炉啦! 万万没想到区块链大佬竟在忙这个...
- 字符常量在C和C++中的区别
- 【bzoj4566】[Haoi2016]找相同字符【后缀自动机】
- iphone固件降级_我在iPhone上装了个安卓
- 求两个数之间的水仙花数.c语言,求水仙花数(C语言/Java)
- 双拼输入法键位图_你知道双拼输入法吗?
- 第十五章,读取txt文件(C++)
- c 语言编程怎样弄循环语句,C 语言循环语句
- 一张图了解js运算符优先级
- matlab线性规划--简单入门
- 163个人电子邮箱如何注册申请,解密163个人邮箱如何注册?
- Torts and Personal Injury
- 深度学习目标检测模型的评价标准-AP与mAP
- Table [xx] contains physical column name referred to by multiple physical column names 错误处理
- go 学习笔记之解读什么是defer延迟函数
- 谷歌中国追求中国式生存
- Python ctypes的byref和pointer区别
- jsp层级选择器_jQuery 选择器
- linux环境下安装node