乐于分享
好东西不私藏

轻松搞定Excel!Java EasyExcel入门与实践指南

轻松搞定Excel!Java EasyExcel入门与实践指南

前言

在 Java 后端开发中,Excel 文件的导入导出是高频业务需求 —— 无论是电商平台的商品批量录入、金融系统的报表生成,还是政务系统的海量数据归档,都离不开对 Excel 的操作。但传统的 POI、JXL 等组件在处理大数据量 Excel 时,极易因 “一次性加载全量数据到内存” 引发 OOM(内存溢出),且 API 繁琐、开发效率低。

阿里巴巴开源的 EasyExcel 组件,正是为解决这些痛点而生,凭借 “低内存、高性能、易上手” 的特性,成为 Java 生态中 Excel 处理的首选工具。

一、简介

EasyExcel 是阿里巴巴 2018 年开源的 Java Excel 处理组件,基于 SAX(Simple API for XML)流式解析模式开发,核心目标是解决传统 Excel 组件的内存占用问题。

与 POI 将整个 Excel 文件加载到内存不同,EasyExcel 采用 “逐行读取、逐行写入” 的方式:读取时仅解析当前行数据并回调处理,写入时仅缓存当前行数据并刷入文件,全程内存占用可控制在几十 MB 级,即使处理百万行数据的 Excel 文件,也能稳定运行。

该组件兼容 Excel 2003(.xls)和 2007+(.xlsx)格式,支持数据校验、单元格样式自定义、多 sheet 页处理、模板导出、动态表头等高级功能,且提供高度封装的 API 和注解,开发者无需关注底层解析细节,大幅降低开发成本。

官网:https://easyexcel.opensource.alibaba.com/

Github:

https://github.com/alibaba/easyexcel

二、发展历程

EasyExcel 的迭代始终围绕 “解决实际痛点、适配主流场景” 展开:

  1. 2018 年:首次开源发布,核心解决 POI 的 OOM 问题,仅提供基础的单 sheet 页导入导出功能;
  2. 2019-2020 年:迭代新增数据校验、单元格样式自定义、多 sheet 页读写、模板导出等功能,完善 API 文档,社区影响力快速扩大;
  3. 2021 年:推出easyexcel-spring-boot-starter,无缝适配 SpringBoot 框架,简化集成流程;
  4. 2022 年至今:优化大数据量分批处理、动态表头生成能力,支持 CSV 格式,修复边缘场景 Bug,成为 Java 领域 Excel 处理的标杆组件。

值得关注的是,EasyExcel在2024年11月宣布逐步进入维护模式,不再主动增加新功能,但仍会维护基础功能和修复bug。原项目作者启动了FastExcel项目,计划提供更强大的功能和性能优化。

https://github.com/meistangtao/fastexcel

三、核心特点

1. 极致低内存,杜绝 OOM

这是 EasyExcel 最核心的优势:基于 SAX 流式解析,不缓存全量数据,仅处理当前行。测试数据显示,处理 100 万行 ×10 列的 Excel 文件,POI 需占用数百 MB 甚至 GB 级内存,而 EasyExcel 仅需约 50MB 内存,彻底避免内存溢出。

2. API 简洁易用,开发效率翻倍

通过注解绑定 Java 实体与 Excel 表头,配合监听接口处理数据,无需手动操作单元格、行、sheet 等底层对象。同等功能下,EasyExcel 的代码量仅为 POI 的 1/3,大幅降低开发门槛。

3. 功能全面,覆盖全场景需求

除基础的导入导出外,还支持:

  • 多 sheet 页的批量读写;
  • 单元格样式(字体、颜色、边框、合并单元格)自定义;
  • 数据校验(必填项、格式校验、自定义规则);
  • 模板导出(填充固定模板生成报表);
  • 动态表头(根据业务动态生成表头);
  • 大数据量分批处理(读取 / 写入时分批入库,避免数据库压力)。

4. 兼容性强,适配无门槛

兼容 Excel 2003/2007+、CSV 等格式,无需额外配置即可自动适配;支持 SpringBoot、Spring MVC 等主流框架,且无第三方强依赖,接入成本极低。

四、应用场景

EasyExcel 几乎覆盖所有 Excel 处理场景,核心应用场景包括:

  1. 批量数据导入:电商商品信息批量录入、企业员工信息批量导入、政务系统表单数据批量采集;
  2. 批量数据导出:销售报表、财务对账表、用户行为分析表等报表生成,海量数据备份导出;
  3. 大数据量处理:百万级订单数据、千万级用户日志的 Excel 解析与入库;
  4. 模板化导出:合同模板、发票模板、通知书模板的动态数据填充;
  5. 多 sheet 页处理:一个 Excel 文件包含 “商品信息”“订单信息”“物流信息” 等多 sheet 页的读写。

五、核心类、方法及注解

1. 核心依赖(SpringBoot 项目)

首先在pom.xml引入依赖(推荐使用最新稳定版):

<!-- EasyExcel核心依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.2</version>
</dependency>
<!-- SpringBoot Starter(简化集成) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel-spring-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!-- Lombok(简化实体类编写,可选) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

2. 核心注解(实体类绑定)

EasyExcel 通过注解将 Java 实体类字段与 Excel 表头绑定,核心注解如下:

注解
作用
示例
@ExcelProperty
绑定 Excel 表头,指定名称、列索引、格式等
@ExcelProperty(value = "用户名", index = 1)
@ExcelIgnore
忽略当前字段,不参与 Excel 读写
@ExcelIgnore private String password;
@DateTimeFormat
格式化日期类型字段(导入导出均生效)
@DateTimeFormat("yyyy-MM-dd HH:mm:ss") private Date createTime;
@NumberFormat
格式化数字类型字段(保留小数、千分位等)
@NumberFormat("#.00") private Double amount;
@ExcelNullable
标记字段可为空(默认必填,用于导入校验)
@ExcelNullable @ExcelProperty("备注") private String remark;
@ExcelValidConstraint
导入数据校验(如长度、正则、最大值等)
@ExcelValidConstraint(maxLength = 20) private String username;

@ExcelProperty:字段映射

最核心的注解,用于映射Java对象属性与Excel列:

publicclassUserData{
// 按索引匹配(从0开始)
@ExcelProperty(index = 0)
private String name;

// 按表头名称匹配
@ExcelProperty("用户年龄")
private Integer age;

// 复杂表头(多级表头)
@ExcelProperty({"基本信息""入职日期"})
private Date joinDate;

// 忽略该字段,不参与读写
@ExcelIgnore
private String internalCode;
}

@DateTimeFormat:日期格式化

控制日期字段的格式转换:

@ExcelProperty("出生日期")
@DateTimeFormat("yyyy年MM月dd日")
private Date birthday;

@ExcelProperty("创建时间")
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
private Date createTime;

@NumberFormat:数字格式化

控制数字字段的格式显示:

@ExcelProperty("销售额")
@NumberFormat("#,##0.00")
private BigDecimal salesAmount;

@ExcelProperty("百分比")
@NumberFormat("0.00%")
private Double successRate;

@ColumnWidth和@ContentStyle:样式控制

设置列宽和单元格样式:

@ExcelProperty("商品名称")
@ColumnWidth(20)  // 列宽20字符
private String productName;

@ExcelProperty("库存数量")
@ContentStyle(dataFormat = 2)  // 数字格式,2代表整数
private Integer stockQuantity;

注解使用示例(实体类) :

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.annotation.valid.ExcelNullable;
import com.alibaba.excel.annotation.valid.ExcelValidConstraint;
import lombok.Data;
import java.util.Date;

@Data
publicclassUserExcelDTO{
// 表头:用户ID,第0列,必填
@ExcelProperty(value = "用户ID", index = 0)
@ExcelValidConstraint(min = 1, message = "用户ID不能为空且大于0")
private Long userId;

// 表头:用户名,第1列,长度不超过20
@ExcelProperty(value = "用户名", index = 1)
@ExcelValidConstraint(maxLength = 20, message = "用户名长度不超过20")
private String username;

// 表头:年龄,第2列,范围1-120
@ExcelProperty(value = "年龄", index = 2)
@ExcelValidConstraint(min = 1, max = 120, message = "年龄需在1-120之间")
private Integer age;

// 表头:注册时间,第3列,日期格式化
@ExcelProperty(value = "注册时间", index = 3)
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
private Date registerTime;

// 表头:余额,第4列,保留2位小数
@ExcelProperty(value = "余额(元)", index = 4)
@NumberFormat("#.00")
private Double balance;

// 表头:备注,第5列,可为空
@ExcelProperty(value = "备注", index = 5)
@ExcelNullable
private String remark;

// 忽略密码字段,不参与Excel读写
@ExcelIgnore
private String password;
}

3. 核心类及方法

EasyExcel 的核心操作均围绕以下类展开,方法简洁且语义化:

核心类
作用
核心方法及示例
EasyExcel
入口类,提供静态方法快速实现读写(推荐优先使用)
读取:EasyExcel.read(file, UserExcelDTO.class, listener).sheet().doRead();
写入:EasyExcel.write(file, UserExcelDTO.class).sheet("用户列表").doWrite(dataList);
ExcelReader
Excel 读取器,手动控制读取流程(复杂场景使用)
ExcelReader reader = EasyExcel.read(file).build();
reader.read(readSheet);
ExcelWriter
Excel 写入器,支持多 sheet、样式自定义(复杂场景使用)
ExcelWriter writer = EasyExcel.write(file).build();
writer.write(data, writeSheet);
ReadListener
读取监听器,处理逐行读取的数据(必须实现)
实现invoke(T data, AnalysisContext context)处理单行数据,doAfterAllAnalysed处理读取完成逻辑
WriteSheet
写入 sheet 页配置,指定名称、索引
WriteSheet sheet = EasyExcel.writerSheet(0, "用户列表").build();

1. EasyExcel类

作为整个库的入口类,提供静态方法用于构建读写操作:

java

// 读取Excel
EasyExcel.read(inputStream, clazz, listener).sheet().doRead();

// 写入Excel
EasyExcel.write(outputStream, clazz).sheet().doWrite(dataList);

2. ExcelReaderBuilder

构建Excel读取器,支持链式调用配置各种参数:

ExcelReaderBuilder readerBuilder = EasyExcel.read();
readerBuilder.file("data.xlsx")
    .head(DemoData.class)
    .registerReadListener(newDemoDataListener())
    .sheet(0)
    .doRead()
;

3. ExcelWriterBuilder

构建Excel写入器,支持多sheet写入和复杂格式设置:

ExcelWriterBuilder writerBuilder = EasyExcel.write();
try (ExcelWriter excelWriter = writerBuilder.file("export.xlsx").build()) {
    WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").head(DemoData.class).build();
    excelWriter.write(dataList, writeSheet);
}

4. ReadListener接口

读取监听器,用于在读取过程中处理数据,支持分批处理和异常捕获:

publicinterfaceReadListener<T{
// 每解析一行数据调用一次
voidinvoke(T data, AnalysisContext context);

// 所有数据解析完成后调用
voiddoAfterAllAnalysed(AnalysisContext context);

// 异常处理
defaultvoidonException(Exception exception, AnalysisContext context){}
}

5.AnalysisEventListener<T> 抽象类

该接口用于在读取 Excel 文件时进行数据处理,类似于事件监听器。通过它可以获取每行的数据并进行处理。

主要方法:

  • invoke(T data, AnalysisContext context) :
    在解析每一行数据时触发。这里的 data 是映射后的 Java 对象。

    @Override
    publicvoidinvoke(MyData data, AnalysisContext context){
    // 处理每一行数据
        System.out.println(data);
    }
  • doAfterAllAnalysed(AnalysisContext context) :
    在所有数据解析完成后触发。

    @Override
    publicvoiddoAfterAllAnalysed(AnalysisContext context){
    // 数据解析完后的处理
    }

六、SpringBoot 下 EasyExcel 实战(从简单到复杂)

以下示例均基于 SpringBoot 2.7.x + EasyExcel 3.3.2 实现,涵盖从基础导入导出到复杂场景的全流程。

1. 简单示例:单 sheet 页基础导入导出

(1)基础导出(下载 Excel 文件)

编写 Controller 接口,实现用户列表导出为 Excel:

import com.alibaba.excel.EasyExcel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@RestController
@RequestMapping("/excel")
publicclassEasyExcelBasicController{

// 模拟生成测试数据
private List<UserExcelDTO> generateTestData(){
        List<UserExcelDTO> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
            UserExcelDTO dto = new UserExcelDTO();
            dto.setUserId((long) i);
            dto.setUsername("测试用户" + i);
            dto.setAge(20 + i);
            dto.setRegisterTime(new Date());
            dto.setBalance(1000.0 + i * 100);
            dto.setRemark("测试备注" + i);
            list.add(dto);
        }
return list;
    }

/**
     * 简单导出:单sheet页,基础样式
     */

@GetMapping("/export/basic")
publicvoidexportBasic(HttpServletResponse response)throws IOException {
// 1. 设置响应头(指定文件格式、编码、下载名称)
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
// 解决文件名中文乱码
        String fileName = URLEncoder.encode("用户列表_基础版""UTF-8").replaceAll("\\+""%20");
        response.setHeader("Content-Disposition""attachment;filename*=UTF-8''" + fileName + ".xlsx");

// 2. EasyExcel写入数据到响应流
        EasyExcel.write(response.getOutputStream(), UserExcelDTO.class)
                .sheet("用户基础信息") // 指定sheet名称
                .doWrite(generateTestData())
// 写入数据列表
    }
}

测试:启动 SpringBoot 项目,访问http://localhost:8080/excel/export/basic,浏览器会自动下载 Excel 文件。

(2)基础导入(上传 Excel 文件)

首先实现读取监听器(处理逐行读取的数据):

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;

@Slf4j
publicclassUserExcelReadListenerextendsAnalysisEventListener<UserExcelDTO{

// 存储读取的数据(可分批处理,避免内存堆积)
private List<UserExcelDTO> dataList = new ArrayList<>(1000);

/**
     * 每读取一行数据,触发该方法
     */

@Override
publicvoidinvoke(UserExcelDTO dto, AnalysisContext context){
        log.info("读取到Excel数据:{}", dto);
        dataList.add(dto);
// 分批处理:每1000条数据批量入库(模拟)
if (dataList.size() >= 1000) {
            batchSave(dataList);
            dataList.clear();
        }
    }

/**
     * 所有数据读取完成后,触发该方法
     */

@Override
publicvoiddoAfterAllAnalysed(AnalysisContext context){
// 处理剩余不足1000条的数据
if (!dataList.isEmpty()) {
            batchSave(dataList);
        }
        log.info("Excel数据读取完成,总计:{}条", dataList.size());
    }

// 模拟批量入库(实际开发中注入Mapper/Service)
privatevoidbatchSave(List<UserExcelDTO> list){
        log.info("批量保存{}条用户数据", list.size());
// TODO: 调用Service写入数据库
    }
}

然后编写导入接口:

import com.alibaba.excel.EasyExcel;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;

@RestController
@RequestMapping("/excel")
publicclassEasyExcelBasicController{
/**
     * 简单导入:上传Excel文件,解析数据
     */

@PostMapping("/import/basic")
public String importBasic(@RequestParam("file") MultipartFile file) throws IOException {
// 校验文件
if (file.isEmpty()) {
return"上传文件不能为空!";
        }
// EasyExcel读取文件,通过监听器处理数据
        EasyExcel.read(file.getInputStream(), UserExcelDTO.classnewUserExcelReadListener())
                .sheet() // 读取第一个sheet页(默认)
                .doRead()
// 执行读取
return"Excel导入成功!";
    }
}

测试:使用 Postman/ApiPost 等工具,以POST方式访问http://localhost:8080/excel/import/basic,上传包含用户数据的 Excel 文件,控制台会打印读取到的数据。

2. 复杂示例 1:多 sheet 页读写

需求:导出一个 Excel 文件,包含 “用户基础信息” 和 “用户订单信息” 两个 sheet 页;导入时读取这两个 sheet 页的数据。

(1)新增订单实体类

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.NumberFormat;
import lombok.Data;

@Data
publicclassOrderExcelDTO{
@ExcelProperty(value = "订单ID", index = 0)
private Long orderId;

@ExcelProperty(value = "用户ID", index = 1)
private Long userId;

@ExcelProperty(value = "订单金额(元)", index = 2)
@NumberFormat("#.00")
private Double orderAmount;

@ExcelProperty(value = "订单状态", index = 3)
private String orderStatus; // 0:待支付,1:已支付,2:已取消
}

(2)多 sheet 页导出接口

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.WriteSheet;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/excel")
publicclassEasyExcelComplexController{

// 模拟生成订单数据
private List<OrderExcelDTO> generateOrderData(){
        List<OrderExcelDTO> list = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
            OrderExcelDTO dto = new OrderExcelDTO();
            dto.setOrderId((long) i);
            dto.setUserId((long) i);
            dto.setOrderAmount(200.0 + i * 50);
            dto.setOrderStatus(i % 3 == 0 ? "2" : (i % 2 == 0 ? "1" : "0"));
            list.add(dto);
        }
return list;
    }

/**
     * 复杂导出:多sheet页
     */

@GetMapping("/export/multi-sheet")
publicvoidexportMultiSheet(HttpServletResponse response)throws IOException {
// 设置响应头(同基础导出)
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        String fileName = URLEncoder.encode("用户订单数据_多sheet版""UTF-8").replaceAll("\\+""%20");
        response.setHeader("Content-Disposition""attachment;filename*=UTF-8''" + fileName + ".xlsx");

// 构建ExcelWriter,手动控制多sheet写入
try (com.alibaba.excel.ExcelWriter writer = EasyExcel.write(response.getOutputStream()).build()) {
// 第一个sheet:用户基础信息
            WriteSheet userSheet = EasyExcel.writerSheet(0"用户基础信息")
                    .head(UserExcelDTO.class)
                    .build()
;
            writer.write(generateTestData(), userSheet);

// 第二个sheet:用户订单信息
            WriteSheet orderSheet = EasyExcel.writerSheet(1"用户订单信息")
                    .head(OrderExcelDTO.class)
                    .build()
;
            writer.write(generateOrderData(), orderSheet);
        }
    }
}

(3)多 sheet 页导入接口

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.metadata.ReadSheet;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;

@RestController
@RequestMapping("/excel")
publicclassEasyExcelComplexController{
/**
     * 复杂导入:读取多sheet页数据
     */

@PostMapping("/import/multi-sheet")
public String importMultiSheet(@RequestParam("file") MultipartFile file) throws IOException {
if (file.isEmpty()) {
return"上传文件不能为空!";
        }

// 构建ExcelReader,手动控制多sheet读取
try (com.alibaba.excel.ExcelReader reader = EasyExcel.read(file.getInputStream()).build()) {
// 读取第一个sheet:用户基础信息
            ReadSheet userSheet = EasyExcel.readSheet(0)
                    .head(UserExcelDTO.class)
                    .registerReadListener(newUserExcelReadListener())
                    .build()
;

// 读取第二个sheet:用户订单信息(需实现OrderExcelReadListener)
            ReadSheet orderSheet = EasyExcel.readSheet(1)
                    .head(OrderExcelDTO.class)
                    .registerReadListener(newOrderExcelReadListener())
                    .build()
;

// 批量读取多个sheet
            reader.read(userSheet, orderSheet);
        }
return"多sheet页Excel导入成功!";
    }
}

注:OrderExcelReadListener的实现逻辑与UserExcelReadListener一致,仅需将泛型改为OrderExcelDTO

3. 复杂示例 2:自定义单元格样式导出

需求:导出 Excel 时,表头设置蓝色背景、加粗字体,数据行水平居中,且 “余额> 1500” 的行字体设为红色。

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

@RestController
@RequestMapping("/excel")
publicclassEasyExcelStyleController{

/**
     * 复杂导出:自定义单元格样式
     */

@GetMapping("/export/custom-style")
publicvoidexportCustomStyle(HttpServletResponse response)throws IOException {
// 设置响应头(同基础导出)
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        String fileName = URLEncoder.encode("用户列表_自定义样式""UTF-8").replaceAll("\\+""%20");
        response.setHeader("Content-Disposition""attachment;filename*=UTF-8''" + fileName + ".xlsx");

// 1. 定义表头样式
        WriteCellStyle headStyle = new WriteCellStyle();
        headStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex()); // 表头背景色:天蓝色
        headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 填充模式:纯色
// 表头字体:加粗、12号字、黑色
        WriteFont headFont = new WriteFont();
        headFont.setBold(true);
        headFont.setFontSize(12);
        headFont.setColor(IndexedColors.BLACK.getIndex());
        headStyle.setWriteFont(headFont);
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 水平居中

// 2. 定义数据行基础样式
        WriteCellStyle contentStyle = new WriteCellStyle();
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 水平居中

// 3. 样式策略(表头+内容)
        HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy(headStyle, contentStyle);

// 4. 自定义行样式处理器(余额>1500的行设为红色字体)
        RowWriteHandler rowStyleHandler = (sheetHolder, tableHolder, row, relativeRowIndex, isHead) -> {
// 跳过表头
if (isHead || relativeRowIndex == null) {
return;
            }
// 获取当前行数据
            UserExcelDTO dto = generateTestData().get(relativeRowIndex);
if (dto.getBalance() > 1500) {
                Workbook workbook = sheetHolder.getSheet().getWorkbook();
// 定义红色字体
                WriteFont redFont = new WriteFont();
                redFont.setColor(IndexedColors.RED.getIndex());
                WriteCellStyle redStyle = new WriteCellStyle();
                redStyle.setWriteFont(redFont);
// 为当前行所有单元格设置红色字体
for (int i = 0; i < row.getLastCellNum(); i++) {
                    Cell cell = row.getCell(i);
if (cell != null) {
                        cell.setCellStyle(styleStrategy.getContentCellStyle(workbook, redStyle));
                    }
                }
            }
        };

// 5. 写入数据,绑定样式
        EasyExcel.write(response.getOutputStream(), UserExcelDTO.class)
                .sheet("用户列表_自定义样式")
                .registerWriteHandler(styleStrategy) // 全局样式
                .registerWriteHandler(rowStyleHandler) // 自定义行样式
                .doWrite(generateTestData())
;
    }
}

4. 复杂示例 3:模板导出

需求:基于预设的 Excel 模板,填充动态数据生成报表(如销售月报)。

(1)准备模板文件

resources/templates目录下创建sales_template.xlsx模板,模板中使用${变量名}作为占位符:

  • 表头:2024 年 ${month} 销售月报
  • 数据区:{salesList.salesNum}、${salesList.amount}
  • 汇总:总销售额:${totalAmount}

(2)模板导出接口

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/excel")
publicclassEasyExcelTemplateController{

/**
     * 复杂导出:模板填充
     */

@GetMapping("/export/template")
publicvoidexportTemplate(HttpServletResponse response)throws IOException {
// 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        String fileName = URLEncoder.encode("销售月报_模板版""UTF-8").replaceAll("\\+""%20");
        response.setHeader("Content-Disposition""attachment;filename*=UTF-8''" + fileName + ".xlsx");

// 1. 读取模板文件
        ClassPathResource resource = new ClassPathResource("templates/sales_template.xlsx");
        InputStream templateStream = resource.getInputStream();

// 2. 准备填充数据
        Map<String, Object> data = new HashMap<>();
        data.put("month""01"); // 月份
        data.put("totalAmount"125000.0); // 总销售额

// 列表数据
        List<Map<String, Object>> salesList = new ArrayList<>();
        Map<String, Object> sales1 = new HashMap<>();
        sales1.put("productName""手机");
        sales1.put("salesNum"50);
        sales1.put("amount"50000.0);
        salesList.add(sales1);

        Map<String, Object> sales2 = new HashMap<>();
        sales2.put("productName""电脑");
        sales2.put("salesNum"30);
        sales2.put("amount"75000.0);
        salesList.add(sales2);
        data.put("salesList", salesList);

// 3. 配置填充策略(列表纵向填充)
        FillConfig fillConfig = FillConfig.builder().direction(com.alibaba.excel.enums.WriteDirectionEnum.VERTICAL).build();

// 4. 填充模板并导出
        EasyExcel.write(response.getOutputStream())
                .withTemplate(templateStream) // 绑定模板
                .sheet()
                .registerFillConfig("salesList", fillConfig) // 列表填充配置
                .doFill(data); // 填充数据
    }
}

七、结束语

EasyExcel 凭借 “低内存、易上手、功能全” 的核心优势,完美解决了传统 Excel 处理组件的痛点,成为 Java 后端处理 Excel 的首选工具。无论是简单的单 sheet 导入导出,还是复杂的多 sheet、自定义样式、模板填充场景,EasyExcel 都能通过简洁的 API 快速实现,大幅提升开发效率。

如果你想获取本文所有示例的完整源码,或者了解 EasyExcel 更多高级用法(如数据校验、动态表头、大数据量分批处理等),欢迎关注我的微信公众号:技海拾贝。公众号会持续分享 Java 开发实战技巧、框架源码解析、高性能架构设计等干货,助力你在技术之路上持续进阶

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 轻松搞定Excel!Java EasyExcel入门与实践指南

猜你喜欢

  • 暂无文章