【经验分享】EasyExcel实现自定义动态化导出excel

news2024/11/5 17:18:19

需求:

        需求涉及保存设备的灵活属性,设备的属性并不固定。允许设备的属性动态扩展。最终数据库采用EAV 模型的设计模式,使用三张表实现:设备表 、属性定义表、属性值表。

        现在堆上述数据进行导入和导出。

解决:

        这里使用EasyExcel,处理数据的导入导出。工具类核心方法如下:

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.merge.LoopMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.stream.Collectors;

/**
     * 自定义导出
     * @param os
     * @param titleList
     * @param mergeTitles
     * @param dataList
     */
    public static void customDynamicExport(OutputStream os, List<String> titleList,
                                           List<String> mergeTitles, List<List<Object>> dataList) {
//        fileName = fileName + ".xlsx";

        // 导出的数据转换为Map数据结构, k -> 标题,v -> 此标题下的数据按顺序
        Map<String, List<Object>> dataMap = new HashMap<>(titleList.size());
        for (int i = 0 ; i < titleList.size(); i++) {
            // 当前列数据
            List<Object> currectTitleDatas = new ArrayList<>();
            for (int j = 0; j < dataList.size(); j++) {
                currectTitleDatas.add(dataList.get(j).get(i));
            }
            dataMap.put(titleList.get(i), currectTitleDatas);
        }

        // **标题信息
        List<List<String>> headTitleInfo = excelTitle(titleList);
        // **数据信息
        List<List<Object>> excelDataInfo = excelData(dataList);

        // **合并单元格信息
        List<LoopMergeStrategy> mergeStrategies = mergeCells(titleList, mergeTitles, dataMap);
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(os);
        if (!CollectionUtils.isEmpty(mergeStrategies)) {
//            log.info("开始进行合并单元格操作 mergeStrategies ===> {}", mergeStrategies);
            mergeStrategies.forEach(m -> {
                excelWriterBuilder.registerWriteHandler(m);
            });
        }

        excelWriterBuilder.head(headTitleInfo).sheet("模板")
            // 当然这里数据也可以用 List<List<String>> 去传入
            .doWrite(excelDataInfo);
    }

    /**
     * **excel标题
     *
     * @param titleList
     * @return
     */
    private static List<List<String>> excelTitle(List<String> titleList) {

        List<List<String>> excelTitleList = new ArrayList<>(titleList.size());
        titleList.forEach(k -> {
            List<String> titles = new ArrayList<>(1);
            titles.add(k);
            excelTitleList.add(titles);
        });
        return excelTitleList;
    }

    private static List<List<Object>> excelData(List<List<Object>> dataList) {
        return dataList;
    }

    private static List<LoopMergeStrategy> mergeCells(List<String> titleList, List<String> mergeTitles, Map<String, List<Object>> dataMap) {

        // 将需要合并的项转换为Map,以便**合并位置
        Map<String, String> mergeTitleMap = mergeTitles.stream().collect(Collectors.toMap(k -> k, k -> k));

        // **合并位置
        Map<String, Integer> mergeLocationMap = new HashMap<>(mergeTitles.size());
        for (int i = 0; i < titleList.size(); i++) {
            if (Objects.nonNull(mergeTitleMap.get(titleList.get(i)))) {
                mergeLocationMap.put(titleList.get(i), i);
            }
        }

        List<LoopMergeStrategy> mergeStrategyList = new ArrayList<>();

        mergeTitles.forEach(k -> {
            // 合并数据所在的列数
            Integer columnIndex = mergeLocationMap.get(k);

            List<Object> mergeDatas = dataMap.get(k);

            // 需要合并的数量
            Integer currectMergeNum = 1;
            for (int m = 0; m < mergeDatas.size(); m++) {
                // 是否存在合并环节
                Boolean isMerge = false;
                if (m != 0 && Objects.equals(mergeDatas.get(m), mergeDatas.get(m - 1))) {
                    isMerge = true;
                    currectMergeNum = currectMergeNum + 1;

                    if (m == mergeDatas.size() - 1) {
                        isMerge = false;
                    }
                }
                if (!isMerge && currectMergeNum > 1) {
                    // 合并的行数
                    Integer eachRow = currectMergeNum;
                    mergeStrategyList.add(new LoopMergeStrategy(eachRow, columnIndex));
                    currectMergeNum = 1;
                }
            }
        });
        return mergeStrategyList;
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】IPC 进程间通信(一):管道(匿名管道命名管道)

✨ 无人扶我青云志&#xff0c;我自踏雪至山巅 &#x1f30f; &#x1f4c3;个人主页&#xff1a;island1314 &#x1f525;个人专栏&#xff1a;Linux—登神长阶 ⛺️ 欢迎关注&#xff1a;&#x1f44d;点赞 &#…

单片机串口接收状态机STM32

单片机串口接收状态机stm32 前言 项目的芯片stm32转国产&#xff0c;国产芯片的串口DMA接收功能测试不通过&#xff0c;所以要由原本很容易配置的串口空闲中断触发DMA接收数据的方式转为串口逐字节接收的状态机接收数据 两种方式各有优劣&#xff0c;不过我的芯片已经主频跑…

信息学科平台系统开发:基于Spring Boot的最佳实践

3系统分析 3.1可行性分析 通过对本基于保密信息学科平台系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于保密信息学科平台系统采用Spring Boot框架&a…

探索 ONLYOFFICE 8.2 版本:更高效、更安全的云端办公新体验

引言 在当今这个快节奏的时代&#xff0c;信息技术的发展已经深刻改变了我们的工作方式。从传统的纸质文件到电子文档&#xff0c;再到如今的云端协作&#xff0c;每一步技术进步都代表着效率的飞跃。尤其在后疫情时代&#xff0c;远程办公成为常态&#xff0c;如何保持团队之间…

51c自动驾驶~合集4

我自己的原文哦~ https://blog.51cto.com/whaosoft/12413878 #MCTrack 迈驰&旷视最新MCTrack&#xff1a;KITTI/nuScenes/Waymo三榜单SOTA paper&#xff1a;MCTrack: A Unified 3D Multi-Object Tracking Framework for Autonomous Driving code&#xff1a;https://gi…

STM32HAL-最简单的长、短、多击按键框架(多按键)

概述 本文章使用最简单的写法实现长、短、多击按键框架,非常适合移植各类型单片机,特别是资源少的芯片上。接下来将在stm32单片机上实现,只需占用1个定时器作为时钟扫描按键即可。 一、开发环境 1、硬件平台 STM32F401CEU6 内部Flash : 512Kbytes,SARM …

【论文精读】LPT: Long-tailed prompt tuning for image classification

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;论文精读_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 摘要 2. …

队列的模拟实现

概念&#xff1a; 队列 &#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为 队尾&#xff08; Tail/Rear &#xff09; 出队列&a…

Centos安装配置Jenkins

下载安装 注意&#xff1a;推荐的LTS版本对部分插件不适配&#xff0c;直接用最新的版本&#xff0c;jenkins还需要用到git和maven&#xff0c;服务器上已经安装&#xff0c;可查看参考文档[1]、[2]&#xff0c;本次不再演示 访问开始使用 Jenkins 下载jenkins 上传至服务器…

在Python中最小化预测函数的参数

在 Python 中&#xff0c;最小化预测函数的参数通常涉及使用优化算法来调整模型的参数&#xff0c;以减少预测误差。下面介绍几种常见的方法来实现这一目标&#xff0c;主要使用 scipy 和 numpy 库。 1、问题背景 我正在尝试通过解决自己想出的问题来学习Python&#xff0c;我…

统信UOS系统应用开发

包括cpu 、内存 、安全等接口描述。 文章目录 一、内存管理非文件形式的内存动态函数库调用接口二、cpu内置安全飞腾国密加速硬件用户态驱动API说明真随机数真随机数三、cpu多核调度cpu亲和性获取接口用于cpu set集操作的相关宏定义一、内存管理 非文件形式的内存动态函数库调…

postman 获取登录接口中的返回token并设置为环境变量的方法 postman script

postman是一个比较方便的API开发调试工具&#xff0c; 我们在访问API时一般都需要设置一个token来对服务进行认证&#xff0c; 这个token一般都是通过登录接口来获取。 这个postman脚本放到登录接口的sctipt--> post-response里面即可将登陆接口中返回的token值设置到postma…

《华为工作法》读书摘记

无论做什么事情&#xff0c;首先要明确的就是做事的目标。目标是引导行动的关键&#xff0c;也是证明行动所具备的价值的前提&#xff0c;所以目标管理成了企业与个人管理的重要组成部分。 很多时候&#xff0c;勤奋、努力并不意味着就一定能把工作做好&#xff0c;也并不意味…

【大语言模型】ACL2024论文-07 BitDistiller: 释放亚4比特大型语言模型的潜力通过自蒸馏

【大语言模型】ACL2024论文-07 BitDistiller: 释放亚4比特大型语言模型的潜力通过自蒸馏 目录 文章目录 【大语言模型】ACL2024论文-07 BitDistiller: 释放亚4比特大型语言模型的潜力通过自蒸馏目录摘要研究背景问题与挑战如何解决创新点算法模型实验效果代码推荐阅读指数&…

Tomcat 和 Docker部署Java项目的区别

在 Java 项目部署中&#xff0c;Tomcat 和 Docker 是两种常见的选择。虽然它们都可以用来运行 Java 应用&#xff0c;但它们在定位、部署方式、依赖环境、资源隔离、扩展性和适用场景等方面有显著区别。 1. 功能定位 1.1 Tomcat Apache Tomcat 是一种轻量级的 Java 应用服务器…

基于SSM的学生选课系统+LW参考示例

系列文章目录 1.基于SSM的洗衣房管理系统原生微信小程序LW参考示例 2.基于SpringBoot的宠物摄影网站管理系统LW参考示例 3.基于SpringBootVue的企业人事管理系统LW参考示例 4.基于SSM的高校实验室管理系统LW参考示例 5.基于SpringBoot的二手数码回收系统原生微信小程序LW参考示…

Java I/O流详解

文章目录 I/O流概念I/O流的分类字节流&#xff08;Byte Streams&#xff09;字节字节流概述方法主要类和继承关系示例代码字节流读取文件 字符流字符流概述子类Reader1.FileReader&#xff1a;2.CharArrayReader&#xff1a;3.StringReader&#xff1a;4.InputStreamReader&…

基于Multisim数字频率计频率范围0-9999HZ电路(含仿真和报告)

【全套资料.zip】数字频率计仿真电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 1.采用纯数字电路&#xff0c;非单片机。 2.频率计测量的频率范围0-9999HZ。 3.使用数码管进行频…

Python画笔案例-095 绘制鼠标画笔

1、绘制 鼠标画笔 通过 python 的turtle 库绘制 鼠标画笔,如下图: 2、实现代码 绘制 鼠标画笔,以下为实现代码: """鼠标画笔.py本程序可以用鼠标指针在屏幕上画画儿。 """ import turtlescreen = turtle.getscreen() screen.setup(

【温酒笔记】SPI

1. SPI基础 物理层 片选线 &#xff1a;选中拉低SCK: 时钟线MOSI:主出从入MISO:主入从出 协议层 CPOL:时钟极性&#xff1a;空闲电平高低 CPHA:时钟相位&#xff1a;第一个还是第二个边沿采样 2. 示例SPI-W25Q16 (见模组分类下文章)