EasyExcel导出自定义表格

谈到新技术,每个人都会有点恐惧,怕处理不好。确实,第一次使用新技术会遇到很多坑,这次使用 EasyExcel 这个新技术去做 excel 导出,还要给表格加样式,遇到不同的版本问题,遇到颜色加错了地方,反正各种效果都打不到自己想要的那种,幸好最终看文档解决了,特此写下这篇博客。

EasyExcel导出自定义表格

  • 一、导入依赖
  • 二、原理分析
  • 三、上代码

一、导入依赖

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>3.3.2</version>
</dependency>

二、原理分析

  1. 对于 read 函数主要通过流操作获取
    在这里插入图片描述
    对于 EasyExcel.read 方法中常用的一个 read 函数:

    java">EasyExcel.read(fileName, head, readListener).sheet().doRead();
    

    三个参数如下:

    • fileName:Excel 文件的路径或输入流。
    • head:Excel 表头对应的实体类,定义了 Excel 表的结构。
    • readListener:数据读取的监听器,定义了读取数据的逻辑。
  2. Excel 表头的实体类

    在读取 Excel 文件时,需要定义一个实体类来映射 Excel 表头,每个字段对应一个表头列。这个实体类用于指定数据在 Java 对象中的存储结构。

    java">public class ExcelData {
        private String name;
        private Integer age;
        // 其他字段...
    
        // 省略 getter 和 setter 方法
    }
    
  3. 数据读取监听器

    EasyExcel 提供了 AnalysisEventListener 类来处理 Excel 数据的读取。

    需要集成该类,并实现 invoke 方法来处理每一行数据的读取逻辑,以及 doAfterAllAnalysed 方法来处理所有数据解析完成后的逻辑。

    public class ExcelDataListener extends AnalysisEventListener<ExcelData> {
    
        @Override
        public void invoke(ExcelData data, AnalysisContext context) {
            // 处理每一行数据的逻辑
            System.out.println("Read data: " + data);
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 所有数据解析完成后的逻辑
        }
    }
    
  4. Excel 写入

    EasyExcel 也提供了写入 Excel 文件的功能。可以使用 EasyExcel.write 方法来配置写入参数,然后调用 sheet 方法指定写入的 sheet,最后调用 doWrite 方法执行写入操作。

    EasyExcel.write(fileName, head).sheet("Sheet1").doWrite(dataList);
    

    三个参数如下:

    • fileName:写入的 Excel 文件路径。
    • head:Excel 表头对应的实体类。
    • dataList:要写入的数据列表。dataList 是一个 List 集合,其中的元素是实体类的对象。
  5. Excel 写入监听器

    写入 Excel 文件时进行一些额外的处理,可以使用写入的监听器 WritHandler。

    public class ExcelWriteHandler implements WriteHandler {
    
        @Override
        public void sheet(int sheetNo, Sheet sheet) {
            // 对每个 sheet 进行处理的逻辑
        }
    
        @Override
        public void row(int rowNum, Row row) {
            // 对每一行进行处理的逻辑
        }
    
        @Override
        public void cell(int cellNum, Cell cell) {
            // 对每个单元格进行处理的逻辑
        }
    }
    

在写入 Excel 文件时,通过 excelWriter.registerWriterHandler( new ExcelWriterHandler() ) 注册写入监听器即可。

三、上代码

先看要求
在这里插入图片描述
其实这里的大部分样式,都可以参考 EasyExcel API 文档

导出

@Override
    public void importUserSign(ImportUserSignReq req, HttpServletResponse response) {
        String projectName = req.getProjectName();
        String time = req.getTime();

        // 查询第一页数据
        List<SignTemplate1> data1 = new ArrayList<>();
        data1.add(new SignTemplate1().setE1("序号").setE2("成员姓名").setE3("签到次数").setE4("补签次数").setE5("签到总工时").setE6("最后签到时间"));
        data1.addAll(getData1(req));
        
        // 查询第二页数据
        List<SignTemplate2> data2 = getData2(req, getData1(req));
        Integer maxRow = data2.stream().map(SignTemplate2::getE2).max(Integer::compare).orElse(0); //获取最大行

        try {
            // 指定文件名
//            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//            response.setCharacterEncoding("utf-8");
//            String fileName = URLEncoder.encode("签到模板导出.xlsx", "UTF-8");
//            response.setHeader("Content-disposition", "attachment;filename*=" + fileName);
            String fileName = "E:\\excel\\" + "签到模板导出" + System.currentTimeMillis() + ".xlsx";

            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();

            // 第一页
            // 自定义头部样式
            WriteCellStyle headWriteCellStyle1 = new WriteCellStyle();
            headWriteCellStyle1.setFillForegroundColor(IndexedColors.GOLD.getIndex()); //背景颜色-黄色
            headWriteCellStyle1.setHorizontalAlignment(HorizontalAlignment.LEFT); //左对齐
            // 自定义内容样式
            WriteCellStyle contentWriteCellStyle1 = new WriteCellStyle();
            // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
            HorizontalCellStyleStrategy style1 = new HorizontalCellStyleStrategy(headWriteCellStyle1, contentWriteCellStyle1);
            // sheet命名
            WriteSheet writeSheet1 = EasyExcel.writerSheet(1, "项目名称")
                    .registerWriteHandler(style1) //自定义策略
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) //自动列宽
                    .head(getHead("【"+projectName+"】", time, 6)) //动态表头
                    .head(SignTemplate1.class)
                    .build();
            // 写入第一页
            excelWriter.write(data1, writeSheet1);

            // 第二页
            // 自定义头部样式
            WriteCellStyle headWriteCellStyle2 = new WriteCellStyle();
            headWriteCellStyle2.setFillForegroundColor(IndexedColors.GOLD.getIndex()); //背景颜色-黄色
            headWriteCellStyle2.setHorizontalAlignment(HorizontalAlignment.LEFT); //左对齐
            // 自定义内容样式
            WriteCellStyle contentWriteCellStyle2 = new WriteCellStyle();
            contentWriteCellStyle2.setHorizontalAlignment(HorizontalAlignment.RIGHT); //右对齐
            // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现
            HorizontalCellStyleStrategy style2 = new HorizontalCellStyleStrategy(headWriteCellStyle2, contentWriteCellStyle2);
            // sheet命名
            WriteSheet writeSheet2 = EasyExcel.writerSheet(2, "签到明细")
                    .registerWriteHandler(style2) //自定义策略
                    .registerWriteHandler(new CustomCellWriteHandler(maxRow, (data2.size()+1))) //自定义动态行/列背景颜色
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) //自动列宽
                    .head(getHead("【"+projectName+"】", time, (data2.size()+1))) //动态表头
                    .build();
            // 写入第二页
            excelWriter.write(dataList(data2, maxRow), writeSheet2).close();

        }catch (Exception e){
            e.printStackTrace();
            throw new CustomException("导出失败");
        }
    }

动态标题头

private List<List<String>> getHead(String projectName, String time, Integer num) {
    List<List<String>> list = new ArrayList<List<String>>();
    for (int i = 0; i < num; i++) {
        list.add(Arrays.asList(projectName, time));
    }
    return list;
}

动态填充数据

private List<List<Object>> dataList(List<SignTemplate2> data2, Integer maxRow) {
    List<List<Object>> list = new ArrayList<>();
    List<Object> row1 = ListUtils.newArrayList(); //第一行
    List<Object> row2 = ListUtils.newArrayList(); //第二行
    row1.add("成员名称");
    row2.add("签到次数");
    for (int i = 0; i <data2.size(); i++) { //行内每一列数据
        row1.add(data2.get(i).getE1());
        row2.add(data2.get(i).getE2());
    }
    list.add(row1);
    list.add(row2);
    for (int i = 0; i < maxRow; i++) {
        List<Object> row3 = ListUtils.newArrayList(); //第三行-多条
        List<Object> row4 = ListUtils.newArrayList(); //第四行-多条
        row3.add(null);
        row4.add(null);
        for (int j = 0; j <data2.size(); j++) { //行内每一列数据
            List<SignTemplate3> eList = data2.get(j).getEList();//当前列的签到集合
            if (i < eList.size()) {
                row3.add(eList.get(i).getE2()+"  "+eList.get(i).getE1());
                row4.add(ObjectUtil.isNotNull(eList.get(i).getE3())?eList.get(i).getE3():"暂无");
            }else {
                row3.add(null);
                row4.add(null);
            }
        }
        list.add(row3);
        list.add(row4);
    }
    return list;
}

自定义动态行/列背景颜色

package com.glbTech.business.dto.req.stat;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.AbstractCellWriteHandler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import lombok.Data;
import org.apache.commons.lang.BooleanUtils;
import org.apache.poi.hssf.usermodel.HSSFPalette;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddressList;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Data
public class CustomCellWriteHandler extends AbstractCellWriteHandler {

    private Integer maxRow;

    private Integer maxCol;

    private final short colorL = IndexedColors.LIME.getIndex(); //绿色
    private final short colorH = IndexedColors.GREY_25_PERCENT.getIndex(); //灰色

    public CustomCellWriteHandler(Integer maxRow, Integer maxCol) {
        this.maxRow = maxRow;
        this.maxCol = maxCol;
    }

    @Override
    public void afterCellDispose(CellWriteHandlerContext context) {
        // 自定义样式处理
        // 当前事件会在 数据设置到poi的cell里面才会回调
        int x = 1;
        for (int i = 4; i < (maxRow+2)*2; i=(x*2)) {
            Cell cell = context.getCell();
            int rowIndex = cell.getRowIndex(); //行
            int cellIndex = cell.getColumnIndex(); //行的列
            // 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not true
            if (BooleanUtils.isNotTrue(context.getHead())) {
                if (cellIndex > 0 && (rowIndex==i || rowIndex==i+1)) {
                    // 拿到poi的workbook
                    Workbook workbook = context.getWriteWorkbookHolder().getWorkbook();
                    // 这里千万记住 想办法能复用的地方把他缓存起来 一个表格最多创建6W个样式
                    // 不同单元格尽量传同一个 cellStyle
                    CellStyle cellStyle = workbook.createCellStyle();
                    //设置颜色
                    if (x%2==0) {
                        cellStyle.setFillForegroundColor(colorL); //绿色
                    }else {
                        cellStyle.setFillForegroundColor(colorH); //灰色
                    }
                    cellStyle.setAlignment(HorizontalAlignment.RIGHT); //右对齐
                    // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND
                    cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                    cell.setCellStyle(cellStyle);
                    // 由于这里没有指定dataformat 最后展示的数据 格式可能会不太正确
                    // 这里要把 WriteCellData的样式清空, 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到
                    // cell里面去 会导致自己设置的不一样(很关键)
                    context.getFirstCellData().setWriteCellStyle(null);
                }
            }
            x++;
        }
    }
}

好事定律:每件事最后都会是好事,如果不是好事,说明还没到最后。

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

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

相关文章

JavaEE企业开发新技术2

目录 2.7 Field类的基本概念 文字性概念描述&#xff1a; Field类 2.8 Field的基本操作-1 2.9 Field的基本操作-2 分析&#xff1a; 2.10 Field 的综合练习 总结&#xff1a; 和equals的区别&#xff1a; 使用 比较 使用equals比较 2.7 Field类的基本概念 文字性…

OpenCV 图像的几何变换

一、图像缩放 1.API cv2.resize(src, dsize, fx0,fy0,interpolation cv2.INTER_LINEAR) 参数&#xff1a; ①src &#xff1a;输入图像 ②dsize&#xff1a;绝对尺寸 ③fx&#xff0c;fy&#xff1a;相对尺寸 ④interpolation&#xff1a;插值方法 2.代码演示 import cv2 …

前端报错404,nginx正常、gateway没有转发请求

问题描述&#xff1a;前端报错 404 Not Found 原因&#xff1a;nacos中对应服务没有上线&#xff0c;下线后&#xff0c;可以启动本地服务&#xff0c;然后在测试上调试代码。&#xff01;&#xff01; 记住重启对应服务&#xff0c;也不会自动上线。

JVM的内存区域

JVM内存区域最粗略的划分可以分为堆和栈&#xff0c;当然&#xff0c;按照虚拟机规范&#xff0c;可以划分为以下几个、区域 Java虚拟机运行时数据区 JVM内存分为线程私有区和线程共享区&#xff0c;其中方法区和堆是线程共享区&#xff0c;虚拟机栈、本地方法栈和程序计数器是…

植物病害识别:YOLO水稻病害识别/分类数据集(2000多张,2个类别,yolo标注)

YOLO水稻病害识别/分类数据集&#xff0c;包含疾病和正常2类&#xff0c;共2000多张图像&#xff0c;yolo标注完整&#xff0c;可直接训练。 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集或其他任何数据集请私信

floodfill算法题目

前言 大家好&#xff0c;我是jiantaoyab&#xff0c;在下面的题目中慢慢体会floodFill算法&#xff0c;虽然是新的算法&#xff0c;但是用的思想和前面的文章几乎一样&#xff0c;代码格式也几乎一样&#xff0c;但不要去背代码 图像渲染 https://leetcode.cn/problems/flood…

事物的传播属性

事务传播属性是Spring框架在处理事务时的一个重要概念&#xff0c;它定义了在事务方法被另一个事务方法调用时&#xff0c;如何处理事务边界的行为。这些属性是通过Spring的Transactional注解中的propagation属性来设置的。下面是几个常见的Spring事务传播属性&#xff1a; *RE…

生成式 AI:使用 Pytorch 通过 GAN 生成合成数据

导 读 生成对抗网络&#xff08;GAN&#xff09;因其生成图像的能力而变得非常受欢迎&#xff0c;而语言模型&#xff08;例如 ChatGPT&#xff09;在各个领域的使用也越来越多。这些 GAN 模型可以说是人工智能/机器学习目前主流的原因&#xff1b; 因为它向每个人&#xff0…

RK3568 xhci主控挂死问题

串口日志 rootjenet:~# [18694.115430] xhci-hcd xhci-hcd.1.auto: xHCI host not responding to stop endpoint command. [18694.125667] xhci-hcd xhci-hcd.1.auto: xHCI host controller not responding, assume dead [18694.125977] xhci-hcd xhci-hcd.1.auto: HC died; c…

微软模拟飞行器回放功能

参考b站up主&#xff0c;欢迎大家去关注&#xff1a;https://www.bilibili.com/video/BV1Z34y1P7zz/?spm_id_from333.880.my_history.page.click&vd_source4e0b40493e2382633fab2ddc1bb1d9cc 下载网址&#xff1a;https://flightsim.to/file/8163/flight-recorder 坠毁检…

嘿!AI 编码新玩法上线!

随着 AI 智能浪潮到来&#xff0c;AI 编码助手成为越来越多开发者的必备工具&#xff0c;将开发者从繁重的编码工作中解放出来&#xff0c;极大地提高了编程效率&#xff0c;帮助开发者实现更快、更好的代码编写。 通义灵码正是这样一款基于阿里云通义代码大模型打造的智能编码…

如何保证消息的顺序性

先看看顺序会错乱的场景&#xff1a;RabbitMQ&#xff1a;一个 queue&#xff0c;多个 consumer&#xff0c;这不明显乱了&#xff1a; 解决方案&#xff1a;

代码背后的女性:突破性别壁垒的技术先驱

个人主页&#xff1a;17_Kevin-CSDN博客 收录专栏&#xff1a;《程序人生》 引言 在计算机科学的历史长河中&#xff0c;有许多杰出的女性为这个领域的发展做出了重要贡献。她们不仅在技术上取得了卓越成就&#xff0c;还打破了性别壁垒&#xff0c;为后来的女性树立了榜样。今…

22 Dytechlab Cup 2022C. Ela and Crickets(思维、找规律、模拟)

思路就是找规律 可以发现&#xff0c;当拐点在角落时的情况和不在角落的情况是不同 当拐点在角落时&#xff0c;只有目标点的横纵坐标其中的一个和它相同时&#xff0c;这时才可能到达。 否则&#xff0c;我们就简单的例子可以看一下&#xff0c;当一个 2 ∗ 2 2*2 2∗2的矩阵的…

伪分布HBase的安装与部署

1.实训目标 &#xff08;1&#xff09;熟悉掌握使用在Linux下安装伪分布式HBase。 &#xff08;2&#xff09;熟悉掌握使用在HBase伪分布式下使用自带Zookeeper。 2.实训环境 环境 版本 说明 Windows 10系统 64位 操作电脑配置 VMware 15 用于搭建所需虚拟机Linux系统 …

PostgreSQL容器安装

docker中的centos7中安装 选择对应的版本然后在容器中的centos7中执行下面命令 但是启动容器的时候需要注意 开启端口映射开启特权模式启动init进程 docker run -itd --name centos-postgresql -p 5433:5432 --privilegedtrue centos:centos7 /usr/sbin/init 启动然后进入后先…

ARMv8/ARMv9架构下特权程序之间的跳转模型与系统启动探析

文章目录 背景1、前言小结&#xff1a; 2、4个特权等级/4个安全状态之间的跳转模型小结&#xff1a; 3、启动时镜像之间的跳转模型小结&#xff1a; 4、runtime程序之间的跳转模型小结&#xff1a; 推荐 背景 ARMv8和ARMv9架构是ARM公司推出的先进处理器架构&#xff0c;被广泛…

华为ce12800交换机m-lag(V-STP模式)配置举例

配置## 标题思路 采用如下的思路配置M-LAG双归接入IP网络&#xff1a; 1.在Switch上配置上行接口绑定在一个Eth-Trunk中。 2.分别在SwitchA和SwitchB上配置V-STP、DFS Group、peer-link和M-LAG接口。 3.分别在SwitchA和SwitchB上配置LACP M-LAG的系统优先级、系统ID。 4.分别在…

粒子群算法优化RBF神经网络气体浓度预测

目录 完整代码和数据下载链接:粒子群算法优化RBF神经网络气体浓度预测,pso-rbf气体浓度预测(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88937920 RBF的详细原理 RBF的定义 RBF理论 易错及常见问题 RBF应用实例,粒子群算法优化R…

植物病害识别:YOLO水稻病害识别数据集(1000多张,3个类别,yolo标注)

YOLO水稻病害识别数据集&#xff0c;包含水稻白叶枯病、稻瘟病、水稻褐斑病3个常见病害类别&#xff0c;共1000多张图像&#xff0c;yolo标注完整&#xff0c;可直接训练。 适用于CV项目&#xff0c;毕设&#xff0c;科研&#xff0c;实验等 需要此数据集或其他任何数据集请私…