1-EasyExcel
EasyExcel的官网:
https://easyexcel.opensource.alibaba.com/
EasyExcel 是阿里巴巴开源的 Excel 读写框架,相比传统 Excel 工具(如 POI 原生 API、JXL 等),它的核心优势直击业务痛点,也是它被广泛使用的根本缘由:
- 极致节省内存,杜绝 OOM(内存溢出)异常这是 EasyExcel 的核心竞争力。传统 POI 采用「DOM 模式」,会将整个 Excel 文件的所有数据(单元格、样式、公式等)一次性加载到 JVM 内存中,当处理大文件(如 10 万行、100 万行数据)时,极易触发OutOfMemoryError,导致应用崩溃。而 EasyExcel 采用「SAX 流式解析模式」,逐行读取 / 写入 Excel 数据,不缓存全部数据到内存,仅保留当前行的解析上下文,即使是 GB 级别的 Excel 文件,也能平稳处理,内存占用始终保持在较低水平。
- 易用性极强,大幅提升开发效率EasyExcel 采用「注解驱动」设计,无需编写复杂的底层解析逻辑(如 POI 需要手动创建工作簿、工作表、遍历单元格、处理数据类型转换等)。仅通过@ExcelProperty等简单注解,即可快速实现 Excel 表头与 Java 实体类字段的映射,开箱即用,大幅减少模板化代码,降低学习成本和开发工作量(对应你之前通过实体类BigDataTableAssetsInfoExcelVo快速接收导入数据的场景)。
- 功能全面,覆盖绝大多数业务场景它并非轻量功能的简化框架,而是覆盖了 Excel 读写的全场景需求,无需额外集成其他工具:
- 支持 Excel 2003(.xls)和 Excel 2007+(.xlsx)两种格式,兼容性拉满;
- 支持复杂表头(合并单元格、多级表头)、自定义单元格样式(行高、列宽、字体、颜色);
- 支持特殊数据类型转换(日期、枚举、数字格式等)
- 支持导入数据校验、导出数据批量填充、模板导出等实用功能;
- 支持同步 / 异步读写,适配高并发场景。
- 兼容性良好,适配主流技术栈完美兼容 JDK 8+(包括你使用的 JDK 11),可无缝集成 Spring Boot、Spring Cloud 等主流 Java 框架,无额外适配成本;依赖简洁,不会引入大量冗余 JAR 包,减少项目依赖冲突风险。
- 兼容性良好,适配主流技术栈完美兼容 JDK 8+(包括你使用的 JDK 11),可无缝集成 Spring Boot、Spring Cloud 等主流 Java 框架,无额外适配成本;依赖简洁,不会引入大量冗余 JAR 包,减少项目依赖冲突风险。
- 官方维护有保障,社区活跃度高作为阿里巴巴开源项目,EasyExcel 有专业团队持续维护更新,及时修复 BUG、优化性能;社区文档完善,问题解决方案丰富,遇到问题时能快速找到参考案例(如你之前遇到的反射权限问题、映射问题,均有成熟解决方案)。
- 性能更优,支撑高并发业务场景在批量读写 Excel 场景下,EasyExcel 的解析 / 导出速度远超传统 POI 原生 API,同时资源占用更低,能够支撑后台批量导出报表、批量导入业务数据等高并发场景的需求。
2-EasyExcel可解决的问题
2-1-传统 POI 工具的核心缺点
Apache POI 是 Java 操作 Excel 的传统主流工具,但它存在诸多难以规避的缺点,在实际项目中容易引发技术问题和开发效率问题:
2-1-1-内存占用极高,处理大文件极易触发 OOM内存溢出
这是 POI 最致命的缺点,也是实际项目中最常遇到的问题。
- POI 的核心解析模式是「DOM 模式」(文档对象模型):读取 Excel 文件时,会将整个 Excel 的所有数据(包括所有单元格、样式、公式、格式信息等)一次性加载到 JVM 内存中,生成完整的对象树(Workbook -> Sheet -> Row -> Cell)。
- 对于小文件(几千行以内),该模式无明显问题;但当处理大文件(如 10 万行、100 万行数据,或多 sheet、大单元格内容的 Excel)时,内存会急剧飙升,很快触发OutOfMemoryError,导致应用崩溃。
- 即使 POI 提供了 SXSSF(流式 XSSF)做优化(仅缓存指定行数的内存数据,多余数据写入临时文件),也存在明显局限:需要手动配置内存窗口大小,临时文件会占用磁盘空间,且仍无法彻底解决超大规模文件的内存问题,同时增加了代码复杂度。
2-1-2-底层 API 繁琐,开发效率极低,代码冗余度高
POI 的 API 设计偏向底层,操作 Excel 需要编写大量模板化、重复性代码,开发成本高、上手难度大:
- 读取 Excel:需要手动创建Workbook(还要区分.xls的HSSFWorkbook和.xlsx的XSSFWorkbook)、Sheet、遍历Row和Cell,手动判断单元格类型(文本、数字、日期、布尔等),手动处理空值和数据类型转换,代码冗长且易出错。
- 写入 Excel:需要手动创建工作表、设置表头、逐行创建单元格、填充数据、手动配置单元格样式(行高、列宽、字体等),即使是简单的导出功能,也需要几十行甚至上百行代码。
- 示例(POI 简单读取 Excel 的冗余代码):需要手动处理格式判断、空值处理,对比 EasyExcel 差距明显。
2-1-3-Excel 格式兼容性差,易出现解析 / 写入异常
POI 对不同版本 Excel 的支持不够友善,手动兼容成本高:
- 对 Excel 2003(.xls)和 Excel 2007+(.xlsx)采用两套完全不同的 API(HSSF 系列 vs XSSF 系列),开发时需要手动判断文件后缀名,选择对应的 API,若判断失误会直接导致解析失败或文件损坏。
- 对非标准格式的 Excel 文件(如部分第三方工具生成的 Excel、带有特殊样式 / 公式的 Excel),解析时容易出现NullPointerException、IllegalStateException等异常,且排查困难。
- 对合并单元格、多级表头的解析支持薄弱,需要手动编写复杂的逻辑来处理单元格合并关系,极易出现字段映射错误。
2-1-4-特殊数据类型转换繁琐,易出错
POI 不提供自动的数据类型映射能力,处理日期、数字、枚举等特殊类型时,需要手动编写转换逻辑:
- 处理日期类型:需要手动判断单元格是否为日期类型,再通过DateUtil.getJavaDate()转换,若 Excel 中的日期格式不统一(如 yyyy-MM-dd、yyyy/MM/dd、带时分秒等),还需要手动适配,容易出现日期转换异常。
- 处理数字类型:Excel 中的数字(如整数、小数)会被解析为Double类型,需要手动转换为Integer、Long等目标类型,容易出现精度丢失或类型转换错误。
- 处理枚举类型:需要手动将 Excel 中的文本值与 Java 枚举值做映射,代码冗余且维护成本高。
2-1-5-复杂业务场景处理难度大,实现成本高
对于实际项目中的复杂 Excel 场景,POI 的实现逻辑极其复杂,开发和维护成本居高不下:
- 复杂表头处理:针对多级表头、合并单元格表头,需要手动遍历表头行,分析单元格的合并范围和对应关系,才能实现表头与 Java 实体类的映射,逻辑繁琐且易出错。
- 模板导出:基于 Excel 模板填充数据导出(如报表导出),POI 需要手动定位模板中的填充位置,处理动态列表、样式保留等问题,代码复杂度极高。
- 自定义样式批量导出:若需要给导出的 Excel 设置统一表头样式、数据行样式、条件格式等,POI 需要手动创建CellStyle、Font等对象,逐个单元格应用样式,代码冗余且性能低下。
2-1-6- 性能表现不佳,难以支撑高并发 / 批量场景
在批量 Excel 读写(如批量导入业务数据、批量导出报表)、高并发访问场景下,POI 的性能短板尤为突出:
- 批量读取 / 写入时,由于内存占用高、API 底层开销大,处理速度缓慢,容易导致服务器线程阻塞。
- 高并发场景下(如多个用户同时导出大报表),POI 会快速耗尽服务器内存和 CPU 资源,导致应用响应缓慢甚至宕机。
2-1-7-代码可维护性差,后期修改成本高
POI 的代码模板化严重,业务逻辑与 Excel 操作逻辑耦合度高,后期维护困难:
- 若需要修改 Excel 表头名称、调整列顺序、增减字段,需要大面积修改 POI 的遍历和映射代码,容易引入新的 BUG。
- 冗余的模板化代码可读性差,新接手的开发人员需要花费大量时间理解代码逻辑,增加项目维护成本。
3-EasyExcel 对应解决方案(精准攻克 POI 痛点)
针对上述 POI 的 7 大核心缺点,EasyExcel 提供了针对性的解决方案,完美规避传统工具的痛点:

补充:关键解决方案的落地示例(直观对比)
- 解决 POI 内存溢出问题:EasyExcel 流式读取无需缓存全量数据
- POI:读取 10 万行数据会一次性加载所有行到内存,触发 OOM;
- EasyExcel:逐行读取,读取一行处理一行,处理完成后释放该行内存,内存占用稳定在几十 MB 以内。
- 解决 POI API 繁琐问题:EasyExcel 几行代码实现 Excel 读取
- POI:需要几十行代码处理 Workbook、Sheet、Row、Cell 的创建和遍历;
- EasyExcel:通过注解映射实体类后,仅需 3 行核心代码即可读取 Excel 数据(对应你之前的导入场景):
List<BigDataTableAssetsInfoExcelVo> dataList = EasyExcel.read(excelFile)
.head(BigDataTableAssetsInfoExcelVo.class)
.sheet()
.doReadSync();
- 解决 POI 数据转换繁琐问题:EasyExcel 注解自动转换日期
- POI:需要手动判断单元格类型,手动转换日期格式,代码冗余;
- EasyExcel:仅需添加注解,自动完成 Excel 日期与 Java 字符串 / Date 类型的转换:
@ExcelProperty("ddl变更时间")
@DateTimeFormat("yyyy-MM-dd HH:mm:ss") // 自动按该格式转换
private String ddlChangeTime;
3-常用导出工具的工作原理

1. 输入源处理:数据初始加载逻辑
通用逻辑:接收外部.xlsx 文件,完成初始读取准备。
POI 原理:采用 “全量内存加载” 策略,直接将整个 xlsx 文件读取至 JVM 堆内存,后续所有操作均基于内存中的文件副本。
EasyExcel 原理:采用 “磁盘缓存加载” 策略,仅读取文件元信息,将文件主体内容暂存至磁盘临时缓存区,不直接占用堆内存。
2. 解压缩环节:资源提取核心差异
通用逻辑:解压 xlsx 压缩包,提取内部 XML 及关联资源,为后续解析做准备。
POI 原理:内存全量解压。解压过程在内存中完成,将压缩包内所有 XML 文件、样式资源一次性加载到内存,形成完整资源集合,供后续解析调用。
EasyExcel 原理:磁盘流式解压。基于解析进度按需解压,仅将当前待处理的目标 XML 文件(如当前解析的 sheet 对应的 xml)解压至磁盘临时文件,无需加载全部资源,从源头控制内存占用。
3. XML 解析环节:数据读取底层机制
通用逻辑:通过 XML 解析器读取解压后的表格数据,此环节是内存控制的核心节点。
POI 原理:虽基于 SAX(事件驱动型解析)框架,但未完全遵循流式特性,而是先将整个 XML 文件加载到内存并构建 DOM 树(文档对象模型),再遍历 DOM 树提取数据。DOM 树需占用大量内存存储节点关系,导致内存消耗随 XML 文件体积线性增长。
EasyExcel 原理:纯 SAX 流式解析。完全遵循事件驱动模型,从磁盘逐行读取 XML 数据流,每解析一行数据就触发对应事件(如 “行开始”“单元格数据”“行结束”),仅在内存中暂存当前行数据,不构建完整 DOM 树,实现 “读一行、处理一行、释放一行”。
4. 模型转换环节:数据对象映射逻辑
通用逻辑:将 XML 原始数据转换为程序可操作的对象模型,消除原始 XML 的格式依赖。
POI 原理:构建 “层级化全量内存对象”。将数据映射为Workbook(工作簿)→Sheet(工作表)→Row(行)→Cell(单元格)的完整对象树,所有对象全量存储在内存中,支持随机访问任意单元格数据,但内存开销极大。
EasyExcel 原理:实现 “逐行实体映射”。通过@ExcelProperty等注解,将解析到的每一行数据即时映射为单个 Java 实体类对象,内存中仅保留当前行对应的实体对象,解析完成后立即释放内存,无冗余对象存储。
5. 结果返回环节:数据交付模式差异
通用逻辑:将处理后的数据交付给上层应用,支撑业务操作。
POI 原理:全量一次性返回。待所有数据解析完成并构建完对象树后,将整个Workbook对象或Sheet对象一次性返回,上层应用可随机操作任意数据,但需等待全量解析完成。
EasyExcel 原理:流式逐行返回。基于监听器(Listener)模式,每解析完成一行并转换为实体类后,立即通过监听器回调将数据返回给上层应用,实现 “解析与业务处理并行”,无需等待全量数据。
10-Springboot集成Easyexcel实现导出功能
10-1-添加pom依赖
<properties>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<xxl-job.version>2.4.0</xxl-job.version>
<cglib.version>3.3.0</cglib.version>
<easyexcel.version>2.2.10</easyexcel.version>
</properties>
<!-- easyexcel begin -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
<exclusions>
<exclusion>
<artifactId>cglib</artifactId>
<groupId>cglib</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>${cglib.version}</version>
</dependency>
<!-- easyexcel end -->
10-2-配置Excel的目录
#Excel存储目录
excel:
# 方式1-绝对路径(Win D:excel_files;Linux /usr/local/excel_files)
dir: D:excel_files
# 方式2:相对路径(项目根目录下的 excel_files 文件夹,推荐开发环境使用)
# dir: ./excel_files
name: 数据资产录入_表资产
10-3-创建EasyExcel的工具类-ExcelUtil
package com.cn.bp.bmp.file.service.biz;
import com.cn.bp.bmp.file.service.application.command.response.ExcelVo;
import com.cn.bp.bmp.file.service.interfaces.utils.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Excel业务处理层
* 调用ExcelUtil实现指定目录Excel读取、导出等功能
*/
@Slf4j
@Service
public class ExcelBizService {
// 从配置文件注入指定Excel目录
@Value("${excel.dir}")
private String excelLocalDir;
/**
* 读取指定目录下的单个Excel文件
* @param excelFileName Excel文件名(如:user_data.xlsx)
* @return Excel数据列表
* @throws IOException 读取异常
*/
public List<ExcelVo> readLocalExcel(String excelFileName) throws IOException {
// 1. 拼接本地Excel文件完整路径(自动适配Windows/Linux路径分隔符)
String localExcelPath = excelLocalDir + File.separator + excelFileName;
log.info("准备读取本地Excel文件:{}", localExcelPath);
// 2. 调用扩展后的ExcelUtil方法读取本地文件
List<ExcelVo> excelVoList = ExcelUtil.importLocalExcel(localExcelPath, ExcelVo.class);
// 3. 打印读取结果(可根据业务需求做后续处理:如入库、数据校验等)
log.info("本地Excel文件读取成功,共读取{}条数据", excelVoList.size());
return excelVoList;
}
/**
* 可选:读取指定目录下所有Excel文件
*/
public void readAllLocalExcel() {
File dirFile = new File(excelLocalDir);
// 校验目录是否存在
if (!dirFile.exists() || !dirFile.isDirectory()) {
log.error("指定的Excel目录不存在:{}", excelLocalDir);
return;
}
// 过滤出.xlsx/.xls格式文件
File[] excelFiles = dirFile.listFiles((file, name) ->
name.endsWith(".xlsx") || name.endsWith(".xls")
);
if (excelFiles == null || excelFiles.length == 0) {
log.warn("指定Excel目录下无有效Excel文件:{}", excelLocalDir);
return;
}
// 遍历读取所有Excel文件
for (File file : excelFiles) {
try {
List<ExcelVo> excelVoList = ExcelUtil.importLocalExcel(file.getAbsolutePath(), ExcelVo.class);
log.info("读取Excel文件{}成功,数据量:{}", file.getName(), excelVoList.size());
} catch (IOException e) {
log.error("读取Excel文件{}失败", file.getName(), e);
}
}
}
}
10-4-创建接收Excel数据的Vo
package com.cn.bp.bmp.file.service.application.command.response;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* @description: Bigdatatableassetsinfo实体类
* @author Mindy
*/
@HeadRowHeight(value = 40)
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "BigDataTableAssetsInfoExcelVo ")
public class BigDataTableAssetsInfoExcelVo implements Serializable {
/**
* 负责人
*/
@ExcelProperty(value = "负责人", index = 0)
@ColumnWidth(value = 20)
@ApiModelProperty(value = "负责人", name = "owner")
private String owne;
/**
* 业务系统
*/
@ExcelProperty(value = "业务系统", index = 1)
@ColumnWidth(value = 20)
@ApiModelProperty(value = "业务系统", name = "businessSystem")
private String businessSystem;
/**
* 业务域/主题域
*/
@ExcelProperty(value = "业务域/主题域", index = 2)
@ColumnWidth(value = 20)
@ApiModelProperty(value = "业务域/主题域", name = "businessDomain")
private String businessDomain;
/**
* 表名
*/
@ExcelProperty(value = "表名", index = 4)
@ColumnWidth(value = 20)
@ApiModelProperty(value = "表名", name = "tableName")
private String tableName;
/**
* 描述
*/
@ExcelProperty(value = "描述", index = 5)
@ColumnWidth(value = 20)
@ApiModelProperty(value = "描述", name = "description")
private String description;
/**
* 审批状态
*/
@ExcelProperty(value = "审批状态", index = 14)
@ColumnWidth(value = 20)
@ApiModelProperty(value = "审批状态", name = "approvalStatus")
private String approvalStatus;
/**
* 审批意见
*/
@ExcelProperty(value = "审批意见", index = 15)
@ColumnWidth(value = 20)
@ApiModelProperty(value = "审批意见", name = "approvalComment")
private String approvalComment;
}
10-5-自定义监听器
针对大文件(10 万条 + 数据),需自定义监听器继承 AnalysisEventListener,实现流式批量处理,避免 OOM
package com.cn.bp.bmp.file.service.interfaces.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.cn.bp.bmp.file.service.application.command.response.UserExcelVo;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义Excel监听器(大文件读取专用)
* 特点:流式读取,批量处理,避免内存溢出
*/
@Slf4j
public class UserExcelListener extends AnalysisEventListener<UserExcelVo> {
/**
* 批量处理阈值(每100条数据处理一次,可根据业务调整)
*/
private static final int BATCH_THRESHOLD = 100;
/**
* 临时存储读取到的数据
*/
private List<UserExcelVo> tempDataList = new ArrayList<>(BATCH_THRESHOLD);
/**
* 业务处理接口(通过构造方法传入,避免监听器中无法注入Spring Bean)
*/
private final UserExcelBizService userExcelBizService;
/**
* 构造方法:传入业务服务类
* @param userExcelBizService 业务处理类
*/
public UserExcelListener(UserExcelBizService userExcelBizService) {
this.userExcelBizService = userExcelBizService;
}
/**
* 每条数据读取完成后调用(核心方法1)
* @param data 单条Excel数据
* @param context 分析上下文
*/
@Override
public void invoke(UserExcelVo data, AnalysisContext context) {
log.info("读取到Excel单条数据:{}", data);
// 1. 将单条数据加入临时列表
tempDataList.add(data);
// 2. 达到批量阈值时,执行业务处理并清空临时列表
if (tempDataList.size() >= BATCH_THRESHOLD) {
batchProcessData();
tempDataList.clear();
}
}
/**
* 所有数据读取完成后调用(核心方法2)
* @param context 分析上下文
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 处理剩余的不足批量阈值的数据
if (!tempDataList.isEmpty()) {
batchProcessData();
log.info("Excel所有数据读取完成,剩余数据处理完毕");
} else {
log.info("Excel所有数据读取完成,无剩余数据");
}
}
/**
* 批量处理数据(调用业务层方法)
*/
private void batchProcessData() {
try {
// 调用业务层方法,批量保存/处理数据
userExcelBizService.batchSaveUserExcelData(tempDataList);
log.info("批量处理Excel数据成功,处理条数:{}", tempDataList.size());
} catch (Exception e) {
log.error("批量处理Excel数据失败", e);
throw new RuntimeException("批量处理Excel数据失败:" + e.getMessage());
}
}
}
10-6-业务层接口(统一业务处理)
创建业务层接口,封装 Excel 数据的业务逻辑(如批量保存到数据库)。
package com.cn.bp.bmp.file.service.biz;
import com.cn.bp.bmp.file.service.application.command.response.UserExcelVo;
import java.util.List;
/**
* Excel读取业务处理接口
*/
public interface UserExcelBizService {
/**
* 批量保存Excel读取到的用户数据
* @param userExcelVoList 用户Excel数据列表
*/
void batchSaveUserExcelData(List<UserExcelVo> userExcelVoList);
}
10-7-业务层实现类
package com.cn.bp.bmp.file.service.biz.impl;
import com.cn.bp.bmp.file.service.application.command.response.UserExcelVo;
import com.cn.bp.bmp.file.service.biz.UserExcelBizService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Excel读取业务处理实现类
*/
@Slf4j
@Service
public class UserExcelBizServiceImpl implements UserExcelBizService {
/**
* 批量保存用户Excel数据(实际业务中可替换为保存到数据库等操作)
* @param userExcelVoList 用户Excel数据列表
*/
@Override
public void batchSaveUserExcelData(List<UserExcelVo> userExcelVoList) {
// 模拟业务处理:此处可替换为MyBatis/JPA的批量插入操作
log.info("批量保存用户Excel数据,条数:{},数据详情:{}", userExcelVoList.size(), userExcelVoList);
// 示例:userMapper.batchInsert(userExcelVoList);
}
}
10-8-小文件上传读取
10-8-1-接口层
package com.cn.bp.bmp.file.service.controller;
import com.cn.bp.bmp.file.service.application.command.response.UserExcelVo;
import com.cn.bp.bmp.file.service.biz.UserExcelBizService;
import com.cn.bp.bmp.file.service.interfaces.utils.ExcelUtil;
import lombok.RequiredArgsConstructor;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* Excel读取控制层
*/
@Slf4j
@RestController
@RequestMapping("/api/excel")
@RequiredArgsConstructor // 构造方法注入Bean,避免Autowired
public class ExcelReadController {
private final UserExcelBizService userExcelBizService;
/**
* 场景1:小文件上传读取(同步读取,1万条以内推荐)
* @param file 上传的Excel文件(.xlsx/.xls)
* @return 读取结果
*/
@PostMapping("/read/small/upload")
public String readSmallExcelByUpload(@RequestParam("file") MultipartFile file) {
try {
// 1. 调用ExcelUtil工具类,同步读取Excel数据
List<UserExcelVo> userExcelVoList = ExcelUtil.importData(file, UserExcelVo.class);
log.info("小文件上传读取成功,数据条数:{}", userExcelVoList.size());
// 2. 调用业务层,处理读取到的数据
userExcelBizService.batchSaveUserExcelData(userExcelVoList);
return "小文件上传读取成功!数据条数:" + userExcelVoList.size();
} catch (Exception e) {
log.error("小文件上传读取失败", e);
return "小文件上传读取失败:" + e.getMessage();
}
}
}
10-8-2-测试
- 准备一个少于 1 万条数据的 Excel 文件,表头为「ID、姓名、年龄、薪资、入职时间」;
- 使用 PostMan/ApiPost 等工具,以 POST 方式请求 http://localhost:8080/api/excel/read/small/upload;
- 请求方式选择 form-data,键为 file,值选择准备好的 Excel 文件,发送请求即可。

10-9-小文件本地读取
适用于 本地服务器上的 Excel 文件(非上传),同步读取所有数据,无需前端上传。
10-9-1-接口层
package com.cn.bp.bmp.file.service.biz.impl;
import com.cn.bp.bmp.file.service.application.command.response.UserExcelVo;
import com.cn.bp.bmp.file.service.biz.UserExcelBizService;
import com.cn.bp.bmp.file.service.interfaces.utils.ExcelUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* Excel读取业务处理实现类
*/
@Slf4j
@Service
public class UserExcelBizServiceImpl implements UserExcelBizService {
/**
* 批量保存用户Excel数据(实际业务中可替换为保存到数据库等操作)
* @param userExcelVoList 用户Excel数据列表
*/
@Override
public void batchSaveUserExcelData(List<UserExcelVo> userExcelVoList) {
// 模拟业务处理:此处可替换为MyBatis/JPA的批量插入操作
log.info("批量保存用户Excel数据,条数:{},数据详情:{}", userExcelVoList.size(), userExcelVoList);
// 示例:userMapper.batchInsert(userExcelVoList);
}
/**
* 场景2:本地小文件读取(指定文件路径)
* @param localFilePath 本地Excel文件完整路径(如:D:/test/user.xlsx 或 ./excel/user.xlsx)
* @return 读取结果提示
*/
public String readSmallExcelByLocal(String localFilePath) {
try {
// 1. 调用ExcelUtil工具类,读取本地Excel文件
List<UserExcelVo> userExcelVoList = ExcelUtil.importLocalExcel(localFilePath, UserExcelVo.class);
log.info("本地小文件读取成功,数据条数:{}", userExcelVoList.size());
// 2. 调用业务方法,处理数据
this.batchSaveUserExcelData(userExcelVoList);
return "本地小文件读取成功!数据条数:" + userExcelVoList.size();
} catch (Exception e) {
log.error("本地小文件读取失败,文件路径:{}", localFilePath, e);
return "本地小文件读取失败:" + e.getMessage();
}
}
}
10-9-2-测试
package com.cn.bp.bmp.file.service;
import com.cn.bp.bmp.file.service.biz.UserExcelBizService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ExcelLocalReadTest {
@Autowired
private UserExcelBizService userExcelBizService;
@Test
public void testLocalExcelRead() {
// 本地Excel文件路径
String localFilePath = "./excel/user.xlsx";
// 调用读取方法
String result = ((UserExcelBizServiceImpl) userExcelBizService).readSmallExcelByLocal(localFilePath);
System.out.println(result);
}
}


