当前位置: 首页 > news >正文

【excel】easy excel如何导出动态列

动态也有多重含义:本文将描述两种动态场景下的解决方案

场景一:例如表头第一列固定为动物,且必定有第二列,第二列的表头可能为猫 也可能为狗;这是列数固定,列名不固定的场景;

场景二:更复杂的场景则为 第二列可能为猫 可能为狗,第三列可能为熊,也可能没有第三列,甚至可能会有第四列;这是表头数和列数都不固定的场景;

文章目录

  • 场景一:表头名不固定
  • 场景二:表头名和列数都不固定

场景一:表头名不固定

如下dto所示,content字段对应的表头可能会变换,我们只需要在ExcelProperty注解中使用占位符替代

@HeadRowHeight(30)
@ContentRowHeight(20)
@Data
public class EasyExcelDTO {@ColumnWidth(30)@ExcelProperty("标题")private String title;@ColumnWidth(30)@ExcelProperty("${content}")private String content;}

测试代码:
(其中IoUtil是hutool包的工具类)

    public static void main(String[] args) throws FileNotFoundException {File file = new File(("C:\\Users\\10057\\Desktop\\easy_excel.xls"));List<EasyExcelDTO> res = new ArrayList<>();EasyExcelDTO easyExcelDTO = new EasyExcelDTO();Map<String, String> map = new HashMap<>();// DTO里面有title字段
//        map.put("title","1");// 相当于bean拷贝 (下面这行是cglib里面的代码)BeanMap.create(easyExcelDTO).putAll(map);System.out.println(easyExcelDTO);easyExcelDTO.setContent("666");res.add(easyExcelDTO);ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();JSONObject jsonObject = new JSONObject();jsonObject.put("content", "测试动态标题111");EasyExcel.write(byteArrayOutputStream, EasyExcelDTO.class).sheet().registerWriteHandler(new ExcelHandler(jsonObject)).doWrite(res);FileOutputStream fos = new FileOutputStream(file);IoUtil.copy(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()), fos);}

导出的excel结果示例:可以看到第二列的表头内容成功被替换
在这里插入图片描述

场景二:表头名和列数都不固定

实体类代码:
(实际使用中,将DynamicFieldExcelDTO作为一个基类,而涉及了动态导出的DTO类继承该类 ,例如 AnimalExcelDTO extends DynamicFieldExcelDTO, 这么做的目的 主要是需要设定一个dynamicFields字段,用于处理器里面映射并替换值)

dynamicFields的key值则为表头,value为内容值

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.Data;import java.util.Map;@HeadRowHeight(30)
@ContentRowHeight(20)
@Data
public class DynamicFieldExcelDTO {@ColumnWidth(20)@ExcelProperty("订单编号")private String orderNo;/*** K: 表头  V:值 (注意要加上@ExcelIgnore)*/@ExcelIgnoreprivate Map<String,Object> dynamicFields;
}

测试代码:

/***  动态字段导出方法 (不需要调整之前的固定字段)* usage: exportDTO类 <b><u>已有固定字段</u></b> ,需要新增动态字段(即需要追加动态表头及对应数据)* 模拟数据: orderNo为固定字段,dynamicFields map中的key为动态表头* {@link DynamicColumnWriteHandler}*/@Testpublic void dynamicHeadMixWrite() {String fileName =  "D:\\test\\out.xlsx";List<String> header = Arrays.asList("动态头部1","动态头部2","动态头部3");List<DynamicFieldExcelDTO> dataList = new ArrayList<>();DynamicFieldExcelDTO d1 = new DynamicFieldExcelDTO();d1.setOrderNo("订单111");Map<String, Object> map = new HashMap<>();map.put("动态头部1","aaa111");map.put("动态头部2","aaa222");map.put("动态头部3","aaa333");d1.setDynamicFields(map);DynamicFieldExcelDTO d2 = new DynamicFieldExcelDTO();d2.setOrderNo("订单222");Map<String, Object> map2 = new HashMap<>();map2.put("动态头部1","bbb111");map2.put("动态头部2","bbb222");map2.put("动态头部3","bbb333");d2.setDynamicFields(map2);DynamicFieldExcelDTO d3 = new DynamicFieldExcelDTO();d3.setOrderNo("订单333");Map<String, Object> map3 = new HashMap<>();map3.put("动态头部1","ccc111");map3.put("动态头部2","ccc222");map3.put("动态头部3","ccc333");d3.setDynamicFields(map3);dataList.add(d1);dataList.add(d2);dataList.add(d3);EasyExcel.write(fileName,DynamicFieldExcelDTO.class).registerWriteHandler(new DynamicColumnWriteHandler<>(header,dataList)).sheet().doWrite(dataList);}/*** 如果全部为动态字段 将动态数据传参即可*/@Testpublic void dynamicHeadWrite() {String fileName =  "D:\\test\\out.xlsx";EasyExcel.write(fileName)// 这里放入动态头.head(new ArrayList<>()).sheet("模板")// dataList.doWrite(new ArrayList());}

处理器代码:
主要逻辑是在afterRowDispose方法里面追加cell,并通过判断列数、是否创建过表头,避免重复添加


/*** 动态导出* @author csdn:孟秋与你*/
public class DynamicColumnWriteHandler<T extends DynamicFieldExcelDTO> implements RowWriteHandler {private final List<String> dynamicHeaders;private final List<T> dataList;private ConcurrentHashMap<String,Boolean> isHeaderCreated = new ConcurrentHashMap<>();/*** 用于记录表头列数*/private AtomicInteger totalHeaderColumns = new AtomicInteger(-1);public DynamicColumnWriteHandler(List<String> dynamicHeaders, List<T> dataList) {this.dynamicHeaders = dynamicHeaders;this.dataList = dataList;}@Overridepublic void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) { }@Overridepublic void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer rowIndex, Boolean isHead) {}@Overridepublic void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer rowIndex, Boolean isHead) {if (isHead && rowIndex == 0) {// 获取默认的头部样式CellStyle headerCellStyle = row.getCell(0).getCellStyle();// 创建动态列头部int lastCellIndex = row.getLastCellNum() == -1 ? 0 : row.getLastCellNum();// 记录表头的总列数totalHeaderColumns.set(lastCellIndex + dynamicHeaders.size());for (int i = 0; i < dynamicHeaders.size(); i++) {if (Objects.equals(isHeaderCreated.get(dynamicHeaders.get(i)), Boolean.TRUE)) {continue;}Cell cell = row.createCell(lastCellIndex + i);cell.setCellValue(dynamicHeaders.get(i));// 设置样式cell.setCellStyle(headerCellStyle);isHeaderCreated.put(dynamicHeaders.get(i),Boolean.TRUE);}} else if (!isHead) {// 检查数据行是否超出表头的列数if (row.getLastCellNum() + dynamicHeaders.size() > totalHeaderColumns.get()) {// 如果超出,不写入该行数据return;}// 数据行的写入逻辑if (rowIndex < dataList.size()) {T dto = dataList.get(rowIndex);// k:与表头内容对应Map<String, Object> dynamicFields = dto.getDynamicFields();int lastCellIndex = row.getLastCellNum() == -1 ? 0 : row.getLastCellNum();for (int i = 0; i < dynamicHeaders.size(); i++) {Cell cell = row.createCell(lastCellIndex + i);Object value = dynamicFields.get(dynamicHeaders.get(i));cell.setCellValue(value == null ? "" : value.toString());}}}}}

导出的excel结果示例:可以看到订单编号固定字段,以及其它动态字段 都准确的导出。
在这里插入图片描述

其中处理器写的比较仓促,网上用处理器实现动态列的案例很少,博主自己测试通过,如果发现有问题或者更好的建议 欢迎各位在评论区指出;

如果追求稳妥的话,直接在head里面传动态参数即可
(dynamicHeadWrite 方法,可以参考github中easy excel给出的官方demo ),
缺点就是需要将所有的表头都放到headers里面。

假设有个业务场景:你们项目中已有导出近百个字段的报表功能,这些字段之前都是固定的,突然加了个需求 需要导出数个动态字段; 为了这几个动态字段 把之前所有固定字段都写一遍 存入list中 代码可能会很丑陋, 如:

List headers = new ArrayList();
headers.add("第一列");
headers.add("第二列");
// ...
headers.add("第一百列");
// 动态列
List dynamicColumn = xxx;
headers.addAll(dynamicColumn);String fileName =  "D:\\test\\out.xlsx";
EasyExcel.write(fileName)// 这里放入动态头.head(headers).sheet("模板")// dataList 你的数据.doWrite(dataList);

相关文章:

【excel】easy excel如何导出动态列

动态也有多重含义&#xff1a;本文将描述两种动态场景下的解决方案 场景一&#xff1a;例如表头第一列固定为动物&#xff0c;且必定有第二列&#xff0c;第二列的表头可能为猫 也可能为狗&#xff1b;这是列数固定&#xff0c;列名不固定的场景&#xff1b; 场景二&#xff1…...

[Linux] 进程间通信

进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;是指不同进程之间的数据交换与协作。在Linux中&#xff0c;进程间通信有多种方式&#xff0c;每种方式都有其适用的场景。本文将介绍Linux中常见的几种进程间通信方法&#xff1a;管道&#xff08;Pipe&…...

【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-最大的数

CL13 最大的数(20 分) 输入一个有 n 个无重复元素的整数数组 a&#xff0c;输出数组中最大的数。提示&#xff1a;如使用排序库函数 sort()&#xff0c;需要包含头文件#include 。输入&#xff1a; 第一行是一个正整数 n(2<n<20)&#xff1b; 第二行包含 n 个不重复的整…...

【Linux】sudo make install 命令往系统中安装了什么 指定目录进行安装

前情提要 假如我们通过源码安装的方式&#xff0c;安装一个动态库&#xff0c;风格往往是这样的&#xff1a; # 克隆仓库 git clone https://github.com/xxx.git# 进入仓库目录 cd xxx编译 # ... 可能有一些校验代码完整性的sh命令# 构建 mkdir build cd build cmake ..# 编…...

RT-DETR融合CVPR[2020]轻量化卷积模块Ghost Module模块

RT-DETR使用教程&#xff1a; RT-DETR使用教程 RT-DETR改进汇总贴&#xff1a;RT-DETR更新汇总贴 《GhostNet: More Features from Cheap Operations》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/1911.11907 代码链接&#xff1a;GitHub - huawei-noah/Effici…...

发布rust crate

文章目录 一、cargo构建的配置类型&#xff1a;dev与release两种1.编译级别2.将 crate 发布到 Crates.io对整个库的注释pub use再导出功能发布crates.io 参考 一、cargo构建的配置类型&#xff1a;dev与release两种 $ cargo buildFinished dev [unoptimized debuginfo] targe…...

Sequelize+Sqlite3使用示例

以下是一个简单的示例&#xff0c;展示了如何在Node.js中使用Express框架、Sequelize ORM以及SQLite数据库来构建一个支持RESTful API的Web应用程序。 一&#xff0c;安装必要的npm包&#xff1a; npm install express sequelize sqlite3 body-parser 二&#xff0c;创建Jav…...

MyBatisPlus 用法详解

MyBatisPlus 用法详解 MyBatis-Plus&#xff08;简称MP&#xff09;是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。它提供了丰富的功能&#xff0c;包括强大的CRUD操作、条件构造器、自动填充、分页插件等&…...

强化学习入门笔记(Reinforcement Learning,RL) 强推!

由于本人的近期研究方向涉及到强化学习&#xff0c;本科时已经学习过了&#xff0c;但是感觉还是有些概念和算法没有学懂学透&#xff0c;所以想重新系统性的学习一下&#xff0c;记录了整个学习过程&#xff0c;而且对当时没有理解不是特别深刻的内容有了一些更加深刻的理解&a…...

C++ QT 工具日志异步分批保存

C QT 工具软件一般可以如此实现日志保存&#xff1a; #define THREAD_ID (reinterpret_cast<qulonglong>(QThread::currentThreadId()) & 0x0FFF) #define TIME (QDateTime::currentDateTime().toString("yyyy_MM_dd_hh_mm_ss_zzz"))#define LOGD(msg) qD…...

win32com库基于wps对Word文档的基础操作

win32com库基于wps对Word文档的基础操作 文章目录 win32com库基于wps对Word文档的基础操作新建/打开文档段落操作(Paragraph)字体设置(Font)图表操作(Shape) 参考链接: WAS API手册 新建/打开文档 import win32com import win32com.client as win32 # 启动WPS进程 word_obj …...

Kubernetes 网络之深度探索:网络模型与 CNI 插件

Kubernetes 网络之深度探索:网络模型与 CNI 插件 在 Kubernetes 中,网络是一个至关重要的组成部分。它不仅决定了容器之间如何通信,还影响着整个集群的可扩展性和稳定性。本节课将深入剖析 Kubernetes 的网络模型以及 CNI(Container Network Interface)网络插件。 一、K…...

Go 模块管理教程:go.mod 与依赖版本控制

Go 模块管理教程&#xff1a;go.mod 与依赖版本控制 Go 从版本 1.11 开始引入了 Go Modules&#xff0c;通过 go.mod 文件来管理项目的依赖关系和版本。Go 模块系统大大简化了 Go 项目的依赖管理&#xff0c;解决了之前 GOPATH 模式的许多问题。本教程将介绍如何使用 Go 模块管…...

大数据 ETL + Flume 数据清洗 — 详细教程及实例(附常见问题及解决方案)

大数据 ETL Flume 数据清洗 — 详细教程及实例 1. ETL 和 Flume 概述1.1 ETL&#xff08;Extract, Transform, Load&#xff09;1.2 Flume 概述 2. Flume 环境搭建2.1 下载并安装 Flume2.2 启动 Flume 3. Flume 配置和常见 Source、Sink、Channel3.1 Flume Source3.2 Flume Si…...

鸿蒙next版开发:订阅应用事件(ArkTS)

在HarmonyOS 5.0中&#xff0c;ArkTS提供了强大的应用事件订阅机制&#xff0c;允许开发者订阅和处理系统或应用级别的事件。这一功能对于监控应用行为、优化用户体验和进行性能分析至关重要。本文将详细介绍如何在ArkTS中订阅应用事件&#xff0c;并提供示例代码进行说明。 应…...

F litter 开发之flutter_local_notifications

flutter_local_notifications 消息通知 flutter_local_notifications地址 flutter_local_notifications: ^18.0.1class NotificationHelper {//工厂模式调用该类时&#xff0c;默认调用此方法&#xff0c;将实例对象返回出去static NotificationHelper? _instance null;sta…...

springboot参数校验

springboot 参数校验 Validated 以及 Valid - 唏嘘- - 博客园 SpringBoot参数校验Validated、Valid_springboot validate-CSDN博客...

Spring生态学习路径与源码深度探讨

引言 Spring框架作为Java企业级开发中的核心框架&#xff0c;其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身&#xff0c;更需要深入理解其周边组件和工具&#xff0c;以及源码的底层实现逻辑。本文将从Spring生态的学…...

C++:set详解

文章目录 前言一、set概念介绍二、set的使用1. 插入删除相关2. 查找相关1&#xff09;find2&#xff09;count3&#xff09;lower_bound与upper_bound4&#xff09;equal_range 三、set的值是不能修改的原理四、基于哈希表的set总结 前言 根据应用场景的不同&#xff0c;STL总…...

(一)- DRM架构

一&#xff0c;DRM简介 linux内核中包含两类图形显示设备驱动框架&#xff1a; FB设备&#xff1a;Framebuffer图形显示框架; DRM&#xff1a;直接渲染管理器&#xff08;Direct Rendering Manager&#xff09;&#xff0c;是linux目前主流的图形显示框架&#xff1b; 1&am…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

ArcGIS Pro制作水平横向图例+多级标注

今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作&#xff1a;ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等&#xff08;ArcGIS出图图例8大技巧&#xff09;&#xff0c;那这次我们看看ArcGIS Pro如何更加快捷的操作。…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...