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

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

相关推荐
小沈同学呀21 小时前
创建一个Spring Boot Starter风格的Basic认证SDK
java·spring boot·后端
方圆想当图灵1 天前
如何让百万 QPS 下的服务更高效?
分布式·后端
凤山老林1 天前
SpringBoot 轻量级一站式日志可视化与JVM监控
jvm·spring boot·后端
凡梦千华1 天前
Django时区感知
后端·python·django
Chan161 天前
JVM从入门到实战:从字节码组成、类生命周期到双亲委派及打破双亲委派机制
java·jvm·spring boot·后端·intellij-idea
烈风1 天前
004 Rust控制台打印输出
开发语言·后端·rust
用户21411832636021 天前
用 AI 一键搞定!中医药科普短视频制作升级版
后端
秋难降1 天前
零基础学习SQL(十一):SQL 索引结构|从 B+Tree 到 Hash,面试常问的 “为啥选 B+Tree” 有答案了
数据库·后端·mysql
科兴第一吴彦祖1 天前
在线会议系统是一个基于Vue3 + Spring Boot的现代化在线会议管理平台,集成了视频会议、实时聊天、AI智能助手等多项先进技术。
java·vue.js·人工智能·spring boot·推荐算法