EasyExcel导出自定义表格

news2024/11/15 12:08:55

谈到新技术,每个人都会有点恐惧,怕处理不好。确实,第一次使用新技术会遇到很多坑,这次使用 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++;
        }
    }
}

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

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

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

相关文章

【Echarts】柱状图上方显示数字以及自定义值,标题和副标题居中,鼠标上显示信息以及自定义信息

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是《前端》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的理解和掌握…

四川宏博蓬达法律咨询有限公司:专业可靠,您身边的法律守护者

在现代社会&#xff0c;法律咨询服务的需求日益增长&#xff0c;选择一家专业可靠的法律咨询公司成为了许多企业和个人的当务之急。四川宏博蓬达法律咨询有限公司便是这样一个值得信赖的法律服务提供者&#xff0c;以其专业、高效、贴心的服务&#xff0c;赢得了广大客户的信赖…

什么是Redis的数据分片?

Redis的数据分片(sharding)是一种将一个Redis数据集分割成多个部分&#xff0c;分别存诸在不同的Redis节点上的技术。它可以用于将一个单独的Redis数据库扩展到多个物理机器上&#xff0c;从而提高Redis集群的性能和可扩展性 Redis数据分片的实现方式通常是将数据按照某种规则(…

指令调用模板

也就是这边指令通过id和map会定位到一个结构体&#xff0c;然后这个结构再赋值两个成员&#xff0c;一个是函数一个是指令类型&#xff0c;然后这个函数是模板的实例化 使用的时候就传进去&#xff0c;这只是参数&#xff0c;最开始初始化的时候模板就已经实例化了。然后关于模…

Linux-网络编程报错分析

1【UDP】通信 【No route to host】&#xff1a;没有连接主机的路由 原因分析&#xff1a; 1.没有配置好默认网关地址&#xff0c;计算机上的路由表找不到到目标ip的路由。 解决方法&#xff1a;检查网络配置 2. 解决方法&#xff1a; 3. 解决方法&#xff1a;

Flink 性能优化总结(内存配置篇)

内存配置优化 Flink 内存模型 内存模型详解 进程内存&#xff08;Total Process Memory&#xff09;&#xff1a;Flink 进程内存分为堆上内存和堆外内存&#xff0c;堆上内存和 堆外内存的主要区别在于它们的管理方式不同和使用方式不同&#xff0c;这些会影响到它们的性能和…

uniapp引入jQuery

安装 npm install jquery --saveoryarn add jquery引入 import Vue from vue import jquery from "jquery"; Vue.prototype.$ jquery;<template><view>abc</view> </template><script>export default {data() {return {}}} </scr…

冒泡排序的理解与实现【C语言、C++、java】

冒泡排序介绍 冒泡排序(Bubble Sort)&#xff0c;又被称为气泡排序或泡沫排序。 它是一种较简单的排序算法。它会遍历若干次要排序的数列&#xff0c;每次遍历时&#xff0c;它都会从前往后依次的比较相邻两个数的大小&#xff1b;如果前者比后者大&#xff0c;则交换它们的位…

【生成式AI導論 2024】第4講:訓練不了人工智慧?你可以訓練你自己 (中) — 拆解問題與使用工具

文章目录 我的总结 拆解任务让语言模型检查自己的错误为什么同一个问题每次答案都不同&#xff1f;组合拳使用工具使用工具-搜索引擎-RAG使用工具-文字生图AIGPT4 其他插件 from: https://www.youtube.com/watch?vlwe3_x50_uw 我的总结 复杂任务拆解为多个步骤让模型检查自己…

微服务配置中心

什么是配置中心 配置中心是一种用于管理应用程序或系统配置信息的中央服务。它允许开发人员在多个环境&#xff08;如开发、测试、生产&#xff09;之间共享配置&#xff0c;并且可以在不停止应用程序的情况下动态更新配置。 配置中心是统一管理各种应用配置的工具。它能够集中…

蓝桥杯真题讲解:异或和之和 (拆位、贡献法)

蓝桥杯真题讲解&#xff1a;异或和之和 &#xff08;拆位、贡献法&#xff09; 一、视频讲解二、正解代码 一、视频讲解 蓝桥杯真题讲解&#xff1a;异或和之和 &#xff08;拆位、贡献法&#xff09; 二、正解代码 //拆位考虑 #include<bits/stdc.h> #define endl &…

教你怎么写苹果群控系统!

在数字化时代&#xff0c;随着智能手机的普及和iOS系统的广泛应用&#xff0c;苹果设备的管理和控制变得日益重要&#xff0c;为了满足这一需求&#xff0c;苹果群控系统应运而生。 一、系统概述 苹果群控系统是一种能够对多台苹果设备进行集中管理和控制的软件系统。通过该系…

从GPT入门,到R语言基础与作图、回归模型分析、混合效应模型、多元统计分析及结构方程模型、Meta分析、随机森林模型及贝叶斯回归分析综合应用等专题及实战案例

目录 专题一 GPT及大语言模型简介及使用入门 专题二 GPT与R语言基础与作图&#xff08;ggplot2&#xff09; 专题三 GPT与R语言回归模型&#xff08;lm&glm&#xff09; 专题四 GPT与混合效应模型&#xff08;lmm&glmm&#xff09; 专题五 GPT与多元统计分析&…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:NavDestination)

作为子页面的根容器&#xff0c;用于显示Navigation的内容区。 说明&#xff1a; 该组件从API Version 9开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 该组件从API Version 11开始默认支持安全区避让特性(默认值为&#xff1a;expandSaf…

【STM32】串口助手接受数据是乱码如何解决

第一步 首先判断自己使用的串口助手和工程配置的波特率是否相同&#xff0c;一般都是115200 第二步 如果不是上一条的问题&#xff0c;继续排查&#xff0c;检查时钟问题 打开工程&#xff0c;找到此文件(stm32f10x.h)的这个位置&#xff0c;如工程中未添加&#xff0c;可以从…

【组合递归回溯】【removeLast】Leetcode 39. 组合总和

【组合递归回溯】【removeLast】Leetcode 39. 组合总和 解法1 ---------------&#x1f388;&#x1f388;题目链接&#x1f388;&#x1f388;------------------- 解法1 如果是一个集合来求组合的话&#xff0c;就需要startIndex 例如&#xff1a;77.组合 (opens new windo…

python——By.XPATH

一、大部分元素没有id、name唯一标识&#xff0c;此时就需要通过xpath from selenuim import webdriver from selenuim.webdriver.common.by import Bydriver webdriver.Chrome() driver.maximize_window() driver.get(http://xxx) time.sleep(3)# 通过By.XPATH找到唯一页面元…

WorkPlus移动应用平台,完美的移动化办公体验

在移动办公成为企业工作的主流趋势下&#xff0c;高效的移动应用平台对于提升工作效率和协作能力至关重要。WorkPlus以其卓越的性能和全面的功能&#xff0c;助力企业实现移动办公的新时代。 为何选择WorkPlus作为移动应用平台的新选择&#xff1f;首先&#xff0c;WorkPlus提供…

【SpringCloud微服务全家桶学习笔记-GateWay网关(微服务入口)】

Gateway服务网关 API网关为微服务架构中的服务提供了统一的访问入口&#xff0c;客户端通过API网关访问相关服务。API网关的定义类似于设计模式中的门面模式&#xff0c;它相当于整个微服务架构中的门面&#xff0c;所有客户端的访问都通过它来进行路由及过滤。它实现了请求路…

Postman接口测试基本操作(全)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Postman-获取验证码 需求&#xff1a;使用Postman访问验证码接…