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

前言:

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月底发布),本篇的分享希望可以帮助到有需要的朋友。

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

相关推荐
javachen__3 分钟前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql
uzong6 小时前
技术故障复盘模版
后端
GetcharZp6 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程6 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研6 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi7 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
一只爱撸猫的程序猿7 小时前
使用Spring AI配合MCP(Model Context Protocol)构建一个"智能代码审查助手"
spring boot·aigc·ai编程
甄超锋8 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
阿华的代码王国8 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy8 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程