Spring Boot 集成 EasyExcel 导出 Excel 文件【复杂表头】

news/2025/3/4 17:21:26/

前言:

Excel 导出在项目开发中是一个非常常见的业务场景,通过 Java 相关的类库可以轻松实现 Excel 的读写操作,常见的类库有 Apache POI、EasyPoi 和 EasyExcel,本篇我们要分享的是使用 EasyExcel 完成复杂表头的 Excel 导出,希望可以帮助到有需要的朋友。

模拟业务场景

假设有一个 Excel 的导出需求,表头如下:

在这里插入图片描述

对于这种比较复杂的表头如果实用 Apache POI 来实现会比较的麻烦,这里我们使用 EasyExcel 来实现,将会比较简单。

Spring Boot 集成 EasyExcel

Spring Boot 集成 EasyExcel 其实就是引入依赖即可使用,pom.xml 引入依赖如下:

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

EasyExcel Github 地址

EasyExcel 在 Github 上有 33K Stars,是阿里巴巴开源的,但很遗憾,最近宣布停更了,未来将逐步进入维护模式,将继续修复 Bug,但不再主动新增功能,据了解,EasyExcel 作者玉箫去年已经从阿里离职,最近他宣布启动 EasyExcel-Plus 项目,称这个新版本将在原有的基础上进一步提升性能和扩展功能。

创建 VO 类

在字段名上加上 @ExcelProperty 注解就可以实现上面我们这种较为复杂的 Excel 表头,创建实体类如下:

package com.my.study.pojo.vo;import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;/*** @ClassName: InterviewInformationVO* @Author: Author* @Date: 2024/11/13 19:31* @Description:*/
@Data
@ColumnWidth(20)
public class InterviewInformationVO {@ApiModelProperty("姓名")@ExcelProperty(index = 0, value = {"面试记录表", "姓名"})@ColumnWidth(15)private String name;@ApiModelProperty("年龄")@ExcelProperty(index = 1, value = {"面试记录表", "基本信息", "年龄"})@ColumnWidth(15)private int age;@ApiModelProperty("性别")@ExcelProperty(index = 2, value = {"面试记录表", "基本信息", "性别"})@ColumnWidth(15)private String sex;@ApiModelProperty("籍贯")@ExcelProperty(index = 3, value = {"面试记录表", "基本信息", "籍贯"})@ColumnWidth(15)private String nativePlace;@ApiModelProperty("自我评价")@ExcelProperty(index = 4, value = {"面试记录表", "自我评价"})@ColumnWidth(15)private String selfEvaluation;@ApiModelProperty("沟通能力分")@ExcelProperty(index = 5, value = {"面试记录表", "一轮面试","面试官A","沟通能力分"})@ColumnWidth(15)private int cmmunicationAkillsScore1A;@ApiModelProperty("专业能力分")@ExcelProperty(index = 6, value = {"面试记录表", "一轮面试","面试官A","专业能力分"})@ColumnWidth(15)private int professionalAbilityScore1A;@ApiModelProperty("沟通能力分")@ExcelProperty(index = 7, value = {"面试记录表", "一轮面试","面试官B","沟通能力分"})@ColumnWidth(15)private int cmmunicationAkillsScore1B;@ApiModelProperty("专业能力分")@ExcelProperty(index = 8, value = {"面试记录表", "一轮面试","面试官B","专业能力分"})@ColumnWidth(15)private int professionalAbilityScore1B;@ApiModelProperty("沟通能力分")@ExcelProperty(index = 9, value = {"面试记录表", "二轮面试","面试官A","沟通能力分"})@ColumnWidth(15)private int cmmunicationAkillsScore2A;@ApiModelProperty("专业能力分")@ExcelProperty(index = 10, value = {"面试记录表", "二轮面试","面试官A","专业能力分"})@ColumnWidth(15)private int professionalAbilityScore2A;@ApiModelProperty("沟通能力分")@ExcelProperty(index = 11, value = {"面试记录表", "二轮面试","面试官B","沟通能力分"})@ColumnWidth(15)private int cmmunicationAkillsScore2B;@ApiModelProperty("专业能力分")@ExcelProperty(index = 12, value = {"面试记录表", "二轮面试","面试官B","专业能力分"})@ColumnWidth(15)private int professionalAbilityScore2B;}

注意看我使用在段上的注解 @ExcelProperty(index = 5, value = {“面试记录表”, “一轮面试”,“面试官A”,“沟通能力分”}),这简单的一行就实现了表头的合并处理。

导出代码

导出我们选择使用输出流的方式直接返回,当然也可以导出到指定位置,代码如下:

package com.my.study.controller;import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.my.study.pojo.vo.InterviewInformationVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** @ClassName: MyExcelController* @Author: Author* @Date: 2024/11/13 19:19* @Description:*/
@RestController
@RequestMapping("/excel")
@Api(tags = {"【Excel 导出控制层】"})
@Slf4j
public class MyExcelController {@PostMapping("/export")public void export(HttpServletResponse response) {try {List<InterviewInformationVO> exportList = new ArrayList<>();buildInterviewInformation(exportList);// 文件名中文名需要转义String fileName = URLEncoder.encode("面试记录表-" + DateUtil.format(new Date(), "yyyyMMddHHmmss"), "UTF-8");response.setContentType("application/x-download;charset=utf-8");response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");EasyExcel.write(response.getOutputStream(), InterviewInformationVO.class).sheet("sheet1").doWrite(exportList);} catch (IOException e) {log.error("面试记录表导出失败,失败原因:{}", e.getMessage());e.printStackTrace();}}public void buildInterviewInformation(List<InterviewInformationVO> exportList) {InterviewInformationVO interviewInformationVO = new InterviewInformationVO();interviewInformationVO.setName("张三");interviewInformationVO.setAge(25);interviewInformationVO.setSex("男");interviewInformationVO.setNativePlace("湖北");interviewInformationVO.setSelfEvaluation("我是一个工作狂");interviewInformationVO.setCmmunicationAkillsScore1A(90);interviewInformationVO.setCmmunicationAkillsScore1B(85);interviewInformationVO.setProfessionalAbilityScore1A(90);interviewInformationVO.setProfessionalAbilityScore1B(90);interviewInformationVO.setCmmunicationAkillsScore1A(85);interviewInformationVO.setCmmunicationAkillsScore1B(80);interviewInformationVO.setProfessionalAbilityScore1A(75);interviewInformationVO.setProfessionalAbilityScore1B(80);exportList.add(interviewInformationVO);}}

触发导出,效果如下:

在这里插入图片描述

实现了我们开始说的表头效果,导出结果符合预期。

我们并没有写多少代码,就完成了看起来比较复杂的表头,这就是 EasyExcel 带来效果,当前 EasyExcel 在内存管理上也有显著的优势。

方式二实现表头

上面我们使用注解的方式实现了表头效果,下面我们使用代码的方式完成表头功能,代码如下:

package com.my.study.controller;import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.my.study.pojo.vo.InterviewInformationVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;/*** @ClassName: MyExcelController* @Author: Author* @Date: 2024/11/13 19:19* @Description:*/
@RestController
@RequestMapping("/excel")
@Api(tags = {"【Excel 导出控制层】"})
@Slf4j
public class MyExcelController {@PostMapping("/export2")public void export(HttpServletResponse response) {try {List<InterviewInformationVO> exportList = new ArrayList<>();buildInterviewInformation(exportList);// 文件名中文名需要转义String fileName = URLEncoder.encode("面试记录表-" + DateUtil.format(new Date(), "yyyyMMddHHmmss"), "UTF-8");response.setContentType("application/x-download;charset=utf-8");response.addHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");//EasyExcel.write(response.getOutputStream(), InterviewInformationVO.class).sheet("sheet1").doWrite(exportList);EasyExcel.write(response.getOutputStream())// 这里放入动态头.head(generateExcelHead()).sheet()// 这里传入数据.doWrite(new ArrayList<>());//导出到指定位置/*EasyExcel.write("d:\interview_information.xlsx")// 这里放入动态头.head(generateExcelHead()).sheet()// 这里传入数据.doWrite(new ArrayList<>());*/} catch (IOException e) {log.error("面试记录表导出失败,失败原因:{}", e.getMessage());e.printStackTrace();}}private static List<List<String>> generateExcelHead() {List<List<String>> result = new ArrayList<>();List<String> head0 = new ArrayList<>();head0.add("面试记录表");head0.add("姓名");result.add(head0);List<String> head1 = new ArrayList<>();head1.add("面试记录表");head1.add("基本信息");head1.add("年龄");result.add(head1);List<String> head2 = new ArrayList<>();head2.add("面试记录表");head2.add("基本信息");head2.add("性别");result.add(head2);List<String> head3 = new ArrayList<>();head3.add("面试记录表");head3.add("基本信息");head3.add("籍贯");result.add(head3);List<String> head4 = new ArrayList<>();head4.add("面试记录表");head4.add("自我评价");result.add(head4);List<String> head5 = new ArrayList<>();head5.add("面试记录表");head5.add("一轮面试");head5.add("面试官A");head5.add("沟通能力分");result.add(head5);List<String> head6 = new ArrayList<>();head6.add("面试记录表");head6.add("一轮面试");head6.add("面试官A");head6.add("专业能力分");result.add(head6);List<String> head7 = new ArrayList<>();head7.add("面试记录表");head7.add("一轮面试");head7.add("面试官B");head7.add("沟通能力分");result.add(head7);List<String> head8 = new ArrayList<>();head8.add("面试记录表");head8.add("一轮面试");head8.add("面试官B");head8.add("专业能力分");result.add(head8);List<String> head9 = new ArrayList<>();head9.add("面试记录表");head9.add("二轮面试");head9.add("面试官A");head9.add("沟通能力分");result.add(head9);List<String> head10 = new ArrayList<>();head10.add("面试记录表");head10.add("二轮面试");head10.add("面试官A");head10.add("专业能力分");result.add(head10);List<String> head11 = new ArrayList<>();head11.add("面试记录表");head11.add("二轮面试");head11.add("面试官B");head11.add("沟通能力分");result.add(head11);List<String> head12 = new ArrayList<>();head12.add("面试记录表");head12.add("二轮面试");head12.add("面试官B");head12.add("专业能力分");result.add(head12);return result;}}

触发导出结果如下:

在这里插入图片描述

导出结果符合预期,使用代码来实现表头也很容易,但个人更喜欢使用注解来实现。

总结:本篇简单分享使用 EasyExcel 来实现较为复杂的表头导出 Excel,感谢 EasyExcel 作者的开源,也期待 EasyExcel-Plus 的发布(预计 11月底发布),本篇的分享希望可以帮助到有需要的朋友。

如有不正确的地方欢迎各位指出纠正。


http://www.ppmy.cn/news/1576595.html

相关文章

csrf与ssrf学习笔记

一、CSRF&#xff08;Cross-Site Request Forgery&#xff09; 1. 定义 攻击目标&#xff1a;利用用户已登录的合法身份&#xff0c;在用户不知情的情况下发起恶意请求。 核心条件&#xff1a;受害者需已登录目标系统&#xff0c;且浏览器会自动携带身份凭证&#xff08;如 C…

Spring源码分析の配置类解析

文章目录 前言一、processConfigBeanDefinitions1.1、checkConfigurationClassCandidate1.2、parse1.2.1、处理配置类标记了Component 的情况1.2.2、处理 ComponentScan 注解 总结 前言 在Spring的注解模式中&#xff0c;通常在构造AnnotationConfigApplicationContext时需要传…

vue全局注册组件

1、Vue.component 是 Vue 提供的一个全局 API&#xff0c;用于注册一个全局组件。这意味着你可以在应用的任何地方使用这个组件&#xff0c;而无需再次引入。 使用方法&#xff1a; import Vue from vue; import MyComponent from ./MyComponent.vue;// 注册全局组件 Vue.com…

游戏引擎学习第129天

仓库:https://gitee.com/mrxiao_com/2d_game_3 小妙招: vscode:定位错误行 一顿狂按F8 重构快捷键:F2 重构相关的变量 回顾并为今天的内容做准备 今天的工作主要集中在渲染器的改进上&#xff0c;渲染器现在运行得相当不错&#xff0c;得益于一些优化和组织上的改进。我们计…

蓝桥备赛(七)- 函数与递归(中)

一、函数重载 1.1 重载概念 引入&#xff1a; 比如&#xff1a;如果我们现在想要写一个函数 &#xff0c; 求两个整数的和 &#xff1a; #include <cstdio> #include <iostream> using namespace std;int IntAdd(int x, int y) {return x y; } int main() {in…

图数据库Neo4j面试内容整理-Cypher 查询优化

Cypher 查询优化 是在 Neo4j 中提高查询性能的关键部分。Cypher 是 Neo4j 的查询语言,允许我们通过图的结构进行高效的数据检索。然而,随着数据量的增大和查询复杂度的提高,查询性能可能会变差。为了优化 Cypher 查询,我们可以使用多种策略,包括合理设计查询、利用索引和约…

二氧化钛的制备:高科技背后的简单原理 京煌科技

二氧化钛是什么&#xff1f; 二氧化钛&#xff08;TiO₂&#xff09;是一种广泛应用的白色无机化合物&#xff0c;具有高折射率、强遮盖力和优异的化学稳定性。它不仅是涂料、塑料、纸张等行业的重要原料&#xff0c;还在化妆品、食品、医药等领域发挥着重要作用。本文将深入探…

详解DeepSeek模型底层原理及和ChatGPT区别点

一、DeepSeek大模型原理 架构基础 DeepSeek基于Transformer架构,Transformer架构主要由编码器和解码器组成,在自然语言处理任务中,通常使用的是Transformer的解码器部分。它的核心是自注意力机制(Self - Attention),这个机制允许模型在处理输入序列时,关注序列中不同位…