EasyExcel动态列导出
测试代码地址:https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/easyexcel/dynamiccolumn
官方文档:https://easyexcel.opensource.alibaba.com/docs/2.x/quickstart/write
一、实现方式
1、根据需要导出的列找到返回类对象属性的ExcelProperty和ColumnWidth注解,最终生成需要显示的列名和每列的列宽;
2、根据需要导出的列获取Excel中的行数据;
3、添加自定义单元格拦截策略(实现com.alibaba.excel.write.handler.WriteHandler接口)和数据类型转换策略(实现com.alibaba.excel.converters.Converter接口);
4、创建Excel的Sheet页,设置第一步获取的列宽;
二、代码实现
(一)添加基础数据类型转换器(LocalDate、LocalDateTime、LocalTime、Integer)
package com.xiaobai.easyexcel.dynamiccolumn;import cn.hutool.core.date.DateTime;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;/*** @author wangtw* @ClassName DataConverter* @description: 类型转换器* @date 2024/2/919:23*/
public class DataConverter {public static class CoreConverter<T> implements Converter<T> {private final Class<T> tClass;public CoreConverter(Class<T> tClass) {this.tClass = tClass;}/*** 导出支持的类型* @return*/@Overridepublic Class supportJavaTypeKey() {return tClass;}/*** 导入支持的Excel类型* @return*/@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}/*** 导入类型转换* @param cellData* @param excelContentProperty* @param globalConfiguration* @return* @throws Exception*/@Overridepublic T convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {if (cellData.getData() instanceof LocalDate) {return (T) LocalDate.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd"));}if (cellData.getData() instanceof LocalTime) {return (T) LocalTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("HH:mm:ss"));}if (cellData.getData() instanceof LocalDateTime) {return (T) LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));}if (cellData.getData() instanceof Integer) {return (T) Integer.valueOf(cellData.getStringValue());}return null;}/*** 导出类型转换* @param obj* @param excelContentProperty* @param globalConfiguration* @return* @throws Exception*/@Overridepublic CellData convertToExcelData(T obj, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {if (obj instanceof LocalDate) {return new CellData(((LocalDate) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));}if (obj instanceof LocalDateTime) {return new CellData(((LocalDateTime) obj).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));}if (obj instanceof LocalTime) {return new CellData(((LocalTime) obj).format(DateTimeFormatter.ofPattern("HH:mm:ss")));}if (obj instanceof Integer) {return new CellData(String.valueOf(obj));}return null;}}/*** localDate类型转换*/public static class LocalDateConverter extends CoreConverter<LocalDate> {public LocalDateConverter() {super(LocalDate.class);}}/*** localTime类型转换*/public static class LocalTimeConverter extends CoreConverter<LocalTime> {public LocalTimeConverter() {super(LocalTime.class);}}/*** LocalDateTime类型转换*/public static class LocalDateTimeConverter extends CoreConverter<LocalDateTime> {public LocalDateTimeConverter() {super(LocalDateTime.class);}}/*** Integer*/public static class IntegerConverter extends CoreConverter<Integer> {public IntegerConverter() {super(Integer.class);}}
}
(二)导出实现代码
package com.xiaobai.easyexcel.dynamiccolumn;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.sun.istack.internal.NotNull;
import jdk.nashorn.internal.runtime.regexp.joni.ast.StringNode;
import org.apache.poi.util.IOUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;/*** @author wangtw* @ClassName DynamicColumnExport* @description: EasyExcel动态列导出* @date 2024/2/918:07*/
public class DynamicColumnExport {/**** @param d 查询数据方法参数* @param vClass 返回类型* @param getDataFun 查询数据函数式接口* @param outputStream 输出流* @param includeColumns 需要导出的列* @param writeHandlerList 自定义拦截器* @param converterList 自定义数据格式化转换器* @param <D>* @param <U>* @param <V>*/public static <D, U, V> void export(D d, Class<V> vClass,@NotNull Function<D, List<V>> getDataFun,@NotNull OutputStream outputStream,@NotNull List<String> includeColumns,@Nullable List<? extends WriteHandler> writeHandlerList,@Nullable List<? extends Converter> converterList) {/*** 1、根据需要导出的列获取每列的列名和单元格的列宽*/// 单元格宽度int columnIndex = 0;Map<Integer, Integer> columnWidthMap = new HashMap<>();// 获取表格头List<List<String>> headList = new ArrayList<>();List<Field> columnList = new ArrayList<>();Field[] declaredFields = vClass.getDeclaredFields();for (String includeColumn : includeColumns) {Optional<Field> includeColumnOptional = Arrays.stream(declaredFields).filter(f -> f.getName().equals(includeColumn)).findFirst();if (includeColumnOptional.isPresent()) {Field field = includeColumnOptional.get();field.setAccessible(true);ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);if (!ObjectUtils.isEmpty(excelProperty)) {// 列名String[] columNameArray = excelProperty.value();headList.add(Arrays.asList(columNameArray));// 可导出的列columnList.add(field);// 保存每列的宽度ColumnWidth columnWidth = field.getAnnotation(ColumnWidth.class);columnWidthMap.put(columnWidth == null ? -1 : columnWidth.value(), columnIndex++);}}}/*** 2、根据需要导出的列获取需要显示的数据*/List<List<Object>> exportDataList = new ArrayList<>();// 执行函数式接口获取需要导出的数据List<V> dataCollection = getDataFun.apply(d);for (V v : dataCollection) {// 拼装每行的数据List<Object> dataSubList = new ArrayList<>();for (Field field : columnList) {try {Object columnValue = field.get(v);dataSubList.add(Optional.ofNullable(columnValue).orElse(""));} catch (IllegalAccessException e) {e.printStackTrace();}}exportDataList.add(dataSubList);}// 表格处理策略ExcelWriterBuilder writerBuilder = EasyExcel.write(outputStream).head(headList);if (!ObjectUtils.isEmpty(writeHandlerList)) {writeHandlerList.forEach(writerBuilder::registerWriteHandler);}// 类型转换策略if (ObjectUtils.isEmpty(converterList)) {writerBuilder.registerConverter(new DataConverter.IntegerConverter());writerBuilder.registerConverter(new DataConverter.LocalDateConverter());writerBuilder.registerConverter(new DataConverter.LocalTimeConverter());writerBuilder.registerConverter(new DataConverter.LocalDateTimeConverter());} else {converterList.forEach(writerBuilder::registerConverter);}// 创建Sheet页String sheetName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();// 设置列宽writeSheet.setColumnWidthMap(columnWidthMap);// 把数据写入到Sheet页中ExcelWriter excelWriter = writerBuilder.build();excelWriter.write(exportDataList, writeSheet);// 关闭流excelWriter.finish();// 关闭输出流IOUtils.closeQuietly(outputStream);}
}
三、测试
(一)设置表头和内容策略
/*** 设置颜色* @return*/private WriteHandler setColor() {// 头的策略WriteCellStyle headWriteCellStyle = new WriteCellStyle();// 背景设置为白色headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());WriteFont headWriteFont = new WriteFont();headWriteFont.setFontHeightInPoints((short)18);headWriteCellStyle.setWriteFont(headWriteFont);// 内容的策略WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);WriteFont contentWriteFont = new WriteFont();// 字体大小contentWriteFont.setFontHeightInPoints((short)15);contentWriteCellStyle.setWriteFont(contentWriteFont);// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现HorizontalCellStyleStrategy horizontalCellStyleStrategy =new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);return horizontalCellStyleStrategy;}
(二)设置单元格样式策略
public static class CellHandler implements CellWriteHandler {@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {}@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> list, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {CellStyle cellStyle = cell.getCellStyle();cellStyle.setBorderBottom(BorderStyle.THIN);cellStyle.setBorderTop(BorderStyle.THIN);cellStyle.setBorderLeft(BorderStyle.THIN);cellStyle.setBorderRight(BorderStyle.THIN);}}
(四)实体类
可选择某几种属性进行导出
package com.xiaobai.easyexcel.dynamiccolumn;import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;/*** @author wangtw* @ClassName DynamicData* @description: EasyExcel动态列导出* @date 2024/2/914:59*/
@AllArgsConstructor
@Builder
@Data
public class DynamicData {@ExcelProperty("id")private Integer id;@ColumnWidth(30)@ExcelProperty("姓名")private String realName;@ColumnWidth(30)@ExcelProperty("性别")private String sex;@ColumnWidth(30)@ExcelProperty("年龄")private int age;@ColumnWidth(50)@ExcelProperty("单位名称")private String orgName;@ColumnWidth(50)@ExcelProperty("部门名称")private String deptName;
}
(五)数据准备
private List<DynamicData> getData(String condition) {List<DynamicData> dynamicDataList = new ArrayList<>();for (int i = 0; i < 100; i++) {DynamicData data = DynamicData.builder().id(i).age(random.nextInt(60)).sex("男").realName("王" + i).orgName("单位" + i).deptName("部门" + i).build();dynamicDataList.add(data);}return dynamicDataList;}
(六)测试代码(选择orgName、deptName、realName、sex进行导出)
@Testpublic void exportTest() {File file = new File("测试.xlsx");OutputStream outputStream = null;try {outputStream = new FileOutputStream(file);} catch (FileNotFoundException e) {e.printStackTrace();}List<String> includeColumns = new ArrayList<>();includeColumns.add("orgName");includeColumns.add("deptName");includeColumns.add("realName");includeColumns.add("sex");List<WriteHandler> writeHandlers = new ArrayList<>();writeHandlers.add(setColor());writeHandlers.add(new CellHandler());DynamicColumnExport.export(null, DynamicData.class,this::getData, outputStream, includeColumns, writeHandlers, null);}
(七)效果
相关文章:

EasyExcel动态列导出
测试代码地址:https://gitee.com/wangtianwen1996/cento-practice/tree/master/src/test/java/com/xiaobai/easyexcel/dynamiccolumn 官方文档:https://easyexcel.opensource.alibaba.com/docs/2.x/quickstart/write 一、实现方式 1、根据需要导出的列…...
JAVA面试题11
什么是Java的访问修饰符,并列出它们的作用。 Java的访问修饰符包括public、private、protected和默认。它们的作用如下: public: 可以被任何其他类访问。 private: 只能被所在类访问,其他类无法访问。 protected: 可以被所在类和同一个包中的…...

工业数据采集的时间不确定性及PLC-Recorder的通道偏移功能
目录 一、缘起 二、效果展示 三、设置方法 四、小结 一、缘起 大家都知道采集软件首先要尽可能还原数据原来的状态,给用户提供一个可以信赖的参考。但是,数据采集又有很多随机因素:Windows是一个周期不严格的系统、以太网通讯有时间波动、…...

十五、Object 类
文章目录 Object 类6.1 public Object()6.2 toString方法6.3 hashCode和equals(Object)6.4 getClass方法6.5 clone方法6.6 finalize方法 Object 类 本文为书籍《Java编程的逻辑》1和《剑指Java:核心原理与应用实践》2阅读笔记 java.lang.Object类是类层次结构的根…...

计算机网络——06分组延时、丢失和吞吐量
分组延时、丢失和吞吐量 分组丢失和延时是怎样发生的 在路由器缓冲区的分组队列 分组到达链路的速率超过了链路输出的能力分组等待排到队头、被传输 延时原因: 当当前链路有别的分组进行传输,分组没有到达队首,就会进行排队,从…...

[C#] 如何调用Python脚本程序
为什么需要C#调用python? 有以下几个原因需要C#调用Python: Python拥有丰富的生态系统:Python有很多强大的第三方库和工具,可以用于数据科学、机器学习、自然语言处理等领域。通过C#调用Python,可以利用Python的生态系…...

AlmaLinux更换鼠标样式为Windows样式
文章目录 前言先看看条件与依赖第一步:测试最终效果第二步:使用CursorXP修改鼠标样式CurosrXP安装CursorXP使用 第三步:Linux端环境搭建与命令执行UbuntuFedora其他系统均失败 第四步:应用主题 前言 只不过是突发奇想,…...

BUGKU-WEB 留言板
题目描述 题目无需登录后台!需要xss平台接收flag, http协议需要http协议的xss平台打开场景后界面如下: 解题思路 看到此类的题目,应该和存储型xss有关,也就是将恶意代码保存到服务器端即然在服务器端,那就…...

Linux之动静态库
今天我们来讲动静态库! 首先我们来粗粒度的划分一下动态库和静态库。 动态库就是只有一份库文件,所有想用该库的文件与改库文件建立链接,然后使用。这样可以提高代码复用率,避免重复拷贝产生没必要的内存消耗。 静态库…...
手机常亮屏不自动灭屏
一. 基础知识介绍 1. WakeLock(休眠锁) WakeLock用于保持设备的唤醒状态,有些情况下,即时用户不操作App,我们也需要保持屏幕处于唤醒状态,以保证用户体验,比如视频类APP和计步类APP,…...

JVM(1)基础篇
1 初始JVM 1.1 什么是JVM JVM 全称是 Java Virtual Machine,中文译名 Java虚拟机。JVM 本质上是一个运行在计算机上的程序,他的职责是运行Java字节码文件。 Java源代码执行流程如下: 分为三个步骤: 编写Java源代码文件。 使用…...

相机图像质量研究(12)常见问题总结:光学结构对成像的影响--炫光
系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结:光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结:光学结构对成…...

[OPEN SQL] 删除数据
DELETE语句用于删除数据库表中的数据 本次操作使用的数据库表为SCUSTOM,其字段内容如下所示 航班用户(SCUSTOM) 需要删除以下数据 1.删除单条数据 语法格式 DELETE <dbtab> FROM <wa>. DELETE <dbtab> FROM TABLE <itab>. DELETE FROM &…...

C语言第二十五弹---字符函数和字符串函数(上)
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】 目录 1、字符分类函数 2、字符转换函数 3、strlen的使用和模拟实现 4、strcpy 的模拟实现 5、strcat 的模拟实现 6、strcmp 的模拟实现 7、strncpy 函数的使用 总结…...
寒假学习记录16:Express框架(Node)
后续会补充 1.引入express 1.先下载express框架 创建一个package.json格式的文件,里面写入 {"dependencies": {"express": "~4.16.1" //express版本号} } 然后打开终端输入 npm i 2.引入express模块 const express require(&quo…...

机器学习中的10种非线性降维技术对比总结
降维意味着我们在不丢失太多信息的情况下减少数据集中的特征数量,降维算法属于无监督学习的范畴,用未标记的数据训练算法。 尽管降维方法种类繁多,但它们都可以归为两大类:线性和非线性。 线性方法将数据从高维空间线性投影到低维空间(因此…...
[ubuntu]split命令分割文件
split 命令 $ split --help Usage: split [OPTION]... [INPUT [PREFIX]] Output fixed-size pieces of INPUT to PREFIXaa, PREFIXab, ...; default size is 1000 lines, and default PREFIX is x. With no INPUT, or when INPUT is -, read standard input.Mandatory argume…...

《小强升职记:时间管理故事书》阅读笔记
目录 前言 一、你的时间都去哪儿了 1.1 你真的很忙吗 1.2 如何记录和分析时间日志 1.3 如何找到自己的价值观 二、无压工作法 2.1 传说中的“四象限法则 2.2 衣柜整理法 三、行动时遇到问题怎么办? 3.1 臣服与拖延 3.2 如何做到要事第一? 3.…...

visual studio code could not establish connection to *: XHR failed
vscode远程连接服务器时,输入密码,又重新提示输入密码,就这样循环了好几次,然后会报上述的错误。由于我是window系统,我用cmd,然后ssh */你的IP地址/*发现可以远程到服务器上,但是通过Vscode就不…...
JVM-面试题
一、对象 1、对象创建 类加载检查 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池定位到类的符号引用,并且检查这个符号引用代表的类是否被加载、解析和初始化过。若没有,必须先执行类加载过程。分配内存 类加载检查通过后,jvm将为新生对象分配内存,…...

关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...

YSYX学习记录(八)
C语言,练习0: 先创建一个文件夹,我用的是物理机: 安装build-essential 练习1: 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数
高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...