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、根据需要导出的列找到返回类对象属性的ExcelPropertyColumnWidth注解,最终生成需要显示的列名和每列的列宽;
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
         */
        @Override
        public Class supportJavaTypeKey() {
            return tClass;
        }

        /**
         * 导入支持的Excel类型
         * @return
         */
        @Override
        public CellDataTypeEnum supportExcelTypeKey() {
            return CellDataTypeEnum.STRING;
        }

        /**
         * 导入类型转换
         * @param cellData
         * @param excelContentProperty
         * @param globalConfiguration
         * @return
         * @throws Exception
         */
        @Override
        public 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
         */
        @Override
        public 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 {

        @Override
        public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
        }

        @Override
        public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        }

        @Override
        public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        }

        @Override
        public 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进行导出)

    @Test
    public 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);
    }

(七)效果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【安卓跨程序共享数据,探究ContentProvider】

ContentProvider主要用于在不同的应用程序之间实现数据共享的功能&#xff0c;它提供了一套完整的机制&#xff0c;允许一个程序访问另一个程序中的数据&#xff0c;同时还能保证被访问数据的安全性。 目前&#xff0c;使用ContentProvider是Android实现跨程序共享数据的标准方…

一个小而实用的 Python 包 pangu,实现在中文和半宽字符(字母、数字和符号)之间自动插入空格

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一个小巧的库&#xff0c;可以避免自己重新开发功能。利用 Python 包 pangu&#xff0c;可以轻松实现在 CJK&#xff08;中文、日文、韩文&#xff09;和半宽字符&#xff08;字母、数字和符号&#xf…

代码随想录|day 13

Day 13 又出去玩了 附上一个学习链接&#xff1a;https://www.geeksforgeeks.org 具体页面&#xff1a;Introduction to Binary Tree - Data Structure and Algorithm Tutorials - GeeksforGeeks 一、理论学习 今天是回顾了二叉树中最重要的操作 : 遍历二叉树。 我们可以将…

解决挂梯子 无法正常上网 的问题

方法&#xff1a; 打开 控制面板 &#x1f449; 网络和Internet &#x1f449; Internet选项 &#x1f449; 连接 &#x1f449; 局域网设置 &#x1f449; 代理服务器 &#x1f449; 取消选项 有问题可参考下图

flask+python企业产品订单管理系统938re

在设计中采用“自下而上”的思想&#xff0c;在创新型产品提前购模块实现了个人中心、个体管理、发布企业管理、投资企业管理、项目分类管理、产品项目管理、个体投资管理、企业投资管理、个体订单管理、企业订单管理、系统管理等的功能性进行操作。最终&#xff0c;对基本系统…

面试经典150题——三数之和

​"The road to success and the road to failure are almost exactly the same." - Colin R. Davis 1. 题目描述 2. 题目分析与解析 2.1 思路一——暴力方法 因为三个数相加为0&#xff0c;那么说明其中两个加数的和与另一个加数为相反数则满足题意。所以可以得到…

[职场] 公安管理学就业方向及前景 #媒体#笔记#笔记

公安管理学就业方向及前景 公安管理学是中国普通高等学校本科专业。本专业文理兼收&#xff0c;学制4年&#xff0c;授予法学学士学位。本专业培养掌握马克思主义基本原理&#xff0c;政治坚定&#xff0c;坚持党和国家的路线、方针、政策&#xff0c;具有良好职业素养、科学素…

Python并发编程之多线程

前言 本文介绍并发编程中另一个重要的知识 - 线程。 线程介绍 我们知道一个程序的运行过程是一个进程&#xff0c;在操作系统中每个进程都有一个地址空间&#xff0c;而且每个进程默认有一个控制线程&#xff0c;打个比方&#xff0c;在一个车间中有很多原材料通过流水线加工…

基于查询模板的知识图谱问答系统

目录 前言1 知识图谱问答系统的两个核心问题1.1 问句的表示与语义理解1.2 知识库的映射和匹配 2 问答基本流程2.1 模板生成2.2 模板实例化2.3 查询排序和结果获取 3 模板自动生成3.1 quint方法3.2 对齐任务 4 基于查询模板的知识图谱问答系统优缺点4.1 系统的优点4.2 系统的缺点…

安全的接口访问策略

渗透测试 一、Token与签名 一般客户端和服务端的设计过程中&#xff0c;大部分分为有状态和无状态接口。 一般用户登录状态下&#xff0c;判断用户是否有权限或者能否请求接口&#xff0c;都是根据用户登录成功后&#xff0c;服务端授予的token进行控制的。 但并不是说有了tok…

机器学习:Softmax介绍及代码实现

Softmax原理 Softmax函数用于将分类结果归一化&#xff0c;形成一个概率分布。作用类似于二分类中的Sigmoid函数。 对于一个k维向量z&#xff0c;我们想把这个结果转换为一个k个类别的概率分布p(z)。softmax可以用于实现上述结果&#xff0c;具体计算公式为&#xff1a; 对于…

VMWare虚拟机安装

VMWare虚拟机安装 0.Linux运行平台介绍1. VMWare 虚拟软件安装检查虚拟网卡是否安装 创建VMWare虚拟机对创建虚拟机的内容进行设置挂在要安装的CentOS的ISO文件 0.Linux运行平台介绍 Linux的运行平台一共有两种,其中包括物理机平台和虚拟机平台,在学习阶段当中建议使用虚拟机 …

海康威视球机摄像头运动目标检测、跟踪与轨迹预测

一、总体方案设计 运动目标检测与跟踪方案设计涉及视频流的实时拍摄、目标检测、轨迹预测以及云台控制。以下是四个步骤的详细设计&#xff1a; 1.室内场景视频流拍摄 使用海康威视球机摄像头进行室内视频流的实时拍摄。确保摄像头能覆盖整个室内空间&#xff0c;以便捕捉所…

考研数据结构笔记(7)

循环链表、静态链表、顺序表和链表的比较 循环链表循环单链表循环双链表 静态链表什么是静态链表如何定义一个静态链表&#xff1f;简述基本操作的实现 顺序表和链表的比较逻辑结构物理结构/存储结构数据的运算/基本运算创建销毁增加、删除查找 循环链表 循环单链表 循环双链表…

【浙大版《C语言程序设计实验与习题指导(第4版)》】实验7-1-6 求一批整数中出现最多的个位数字(附测试点)

定一批整数&#xff0c;分析每个整数的每一位数字&#xff0c;求出现次数最多的个位数字。例如给定3个整数1234、2345、3456&#xff0c;其中出现最多次数的数字是3和4&#xff0c;均出现了3次。 输入格式&#xff1a; 输入在第1行中给出正整数N&#xff08;≤1000&#xff0…

Game辅助推广购卡系统全新一键安装版-已激活

(购买本专栏可免费下载栏目内所有资源不受限制,持续发布中,需要注意的是,本专栏为批量下载专用,并无法保证某款源码或者插件绝对可用,介意不要购买) 资源简介 运行环境 PHP5.6~7.0+MYSQL5.6 本程序可配合(伯乐发卡)基础版使用; 界面炫酷大气!程序内核为yunucm…

1.CVAT建项目步骤

文章目录 1. 创建project2. 创建task2.1. label 标签详解2.2.高级配置 Advanced configuration 3. 分配任务4. 注释者规范 CVAT的标注最小单位是Task&#xff0c;每个Task为一个标注任务。 1. 创建project 假设你并不熟悉cvat的标注流程&#xff0c;这里以图像2D目标检测为例进…

13. 串口接收模块的项目应用案例

1. 使用串口来控制LED灯工作状态 使用串口发送指令到FPGA开发板&#xff0c;来控制第7课中第4个实验的开发板上的LED灯的工作状态。 LED灯的工作状态&#xff1a;让LED灯按指定的亮灭模式亮灭&#xff0c;亮灭模式未知&#xff0c;由用户指定&#xff0c;8个变化状态为一个循…

《CSS 简易速速上手小册》第7章:CSS 预处理器与框架(2024 最新版)

文章目录 7.1 Sass&#xff1a;更高效的 CSS 编写7.1.1 基础知识7.1.2 重点案例&#xff1a;主题颜色和字体管理7.1.3 拓展案例 1&#xff1a;响应式辅助类7.1.4 拓展案例 2&#xff1a;深色模式支持 7.2 Bootstrap&#xff1a;快速原型设计和开发7.2.1 基础知识7.2.2 重点案例…

微信小程序的了解和使用

微信小程序 微信小程序的项目组成 pages 文件夹 用于存放所有的小程序页面 logs 文件夹 用于存放所有的日志文件 utils 文件夹 用于存放工具性质的模块 js app.js 小程序的入口文件 app.json 小程序的全局配置文件 app.wxss 全局样式文件 project.config.json 项目配置文…
最新文章