前言

本文将介绍如何通过使用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());}}}

afterRowCreateafterRowDispose方法中的内容很好理解,一个用于控制控制新增序号列的内容,一个用于控制新增列的样式。而beforeRowCreate中的内容则稍微复杂,主要用于给新增的序号列空出位置。同时,由于beforeRowCreate会在每次创建行时均会被调用,但是原始的存储头部信息的Map只需要修改一次,因此这里通过使用init变量控制只会修改一次。

思路

通过查看com.alibaba.excel.write.executor.ExcelWriteAddExecutor类中的addOneRowOfDataToExcel方法源码,可以看到在新增行的前后会分别调用beforeRowCreateafterRowCreate方法,并且在一行数据添加完成后会调用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);
}

而之所以我们修改headMapcontentMap的内容就可以实现最终效果,只需要继续查看该类中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 动态添加自增序号列相关推荐

  1. sql 对查询出的 结果集 添加 自增序号列/排序列

    目录 今天做了一个关于排行榜的功能 参考一: 第一种方法: 第二种方法: 参考二: 对于  变量  i  的分析: 参考三(很全面易懂): 一.变量的定义及使用 二.变量的赋值 三.变量使用小技巧 参 ...

  2. html中列表前面的序号带圆圈,js动态添加带圆圈序号列表方法

    js动态添加带圆圈序号列表方法 发布时间:2021-02-19 11:30:34 来源:亿速云 阅读:69 作者:小新 这篇文章给大家分享的是有关js动态添加带圆圈序号列表方法的内容.小编觉得挺实用的 ...

  3. antd table合并行或者列(动态添加合并行、列)

    antd table合并行或者列(动态添加合并行.列) 表头只支持列合并,使用 column 里的 colSpan 进行设置. 表格支持行/列合并,使用 render 里的单元格属性 colSpan ...

  4. html中列表前面的序号带圆圈,js动态添加带圆圈序号列表的实例代码

    1.先在body里面添加ul标签 2.通过js获取到id等于list的标签 定义一个空字符串用来连接增加的标签,并展示出来 如图的js代码展示的是前三个颜色不同,余下的颜色相同的圆圈序号 functi ...

  5. 如何在Hive、MySQL、Oracle中分别添加自增序号

    最近公司业务涉及到了在相应库中添加自增序号这种操作,闲暇之余,整理如下,仅供参考~ 一.Hive 1.首先在Hive中建立一个测试表 create table xzw(id int, name str ...

  6. iview给table组件添加自增序号

    傻逼了,iview自带了自增序号,还是记录一下吧 template: <Table:columns="informationTable":data="informa ...

  7. EasyExcel动态跨行合并和自定义列宽的策略

    1. 动态表头 2.自定义跨行合并的策略 3.自定义列宽的策略 下载表格的代码 new 出合并单元格的对象 第一个参数写入需要合并的列的字符串列表,会根据相同自动合并 第二个参数写入要合并单元格是第几 ...

  8. MySQL中如何为查询的数据添加自增序号、顺序呢

    背景介绍 很多时候我们在使用mysql查询数据的时候都会遇到一个问题,就是查询出来了一堆数据,但是查询的数据的表并没有序号,然而部分数据库显示工具是有外带序号显示,但是这种序号不是由sql产生的,而是 ...

  9. js动态添加带圆圈序号列表

    1.先在body里面添加ul标签 <!-- 无序列表 --> <ul id="list"></ul> 2.通过js获取到id等于list的标签 ...

最新文章

  1. Qmail的介绍和配置
  2. 6月Top 20榜单出炉啦! 万万没想到区块链大佬竟在忙这个...
  3. 字符常量在C和C++中的区别
  4. 【bzoj4566】[Haoi2016]找相同字符【后缀自动机】
  5. iphone固件降级_我在iPhone上装了个安卓
  6. 求两个数之间的水仙花数.c语言,求水仙花数(C语言/Java)
  7. 双拼输入法键位图_你知道双拼输入法吗?
  8. 第十五章,读取txt文件(C++)
  9. c 语言编程怎样弄循环语句,C 语言循环语句
  10. 一张图了解js运算符优先级
  11. matlab线性规划--简单入门
  12. 163个人电子邮箱如何注册申请,解密163个人邮箱如何注册?
  13. Torts and Personal Injury
  14. 深度学习目标检测模型的评价标准-AP与mAP
  15. Table [xx] contains physical column name referred to by multiple physical column names 错误处理
  16. go 学习笔记之解读什么是defer延迟函数
  17. 谷歌中国追求中国式生存
  18. Python ctypes的byref和pointer区别
  19. jsp层级选择器_jQuery 选择器
  20. linux环境下安装node

热门文章

  1. JPG格式转npy格式
  2. Hadoop大数据生态系统笔记
  3. 071.(3.12)什么人不适合创业
  4. 数据作假,亚马逊关闭5万中国卖家真相
  5. word转html教程,Word转HTML工具Convert Word to HTML安装教程
  6. P4557 [JSOI2018]战争 凸包的闵可夫斯基和
  7. 19.10. Automatic Vacuuming
  8. 团队执行力不够,到底是谁的错?
  9. AVPlayer简单地视频播放器
  10. stata复制excel数据(包含英文字母/中文字符)