Spring Boot中Excel处理完全指南:从基础到高级实践
Excel处理基础知识
1.1 为什么需要在应用中处理Excel文件?
在企业应用开发中,Excel文件处理是一个非常常见的需求,主要用于以下场景:
数据导入:允许用户通过Excel上传批量数据到系统
数据导出:将系统数据导出为Excel供用户下载分析
报表生成:生成复杂的报表并格式化为Excel
数据交换:作为不同系统间交换数据的媒介
批量数据处理:处理大量结构化数据
1.2 Java中的Excel处理库介绍
Java中处理Excel文件的主要库有以下几种:
1.2.1 Apache POI
Apache POI是Java中使用最广泛的Excel处理库,提供了全面的API来创建、读取和修改Office文档。
优点:
功能全面,支持Excel所有功能
支持.xls (HSSF - Excel 97-2003)和.xlsx (XSSF - Excel 2007+)格式
社区活跃,文档丰富
支持公式计算、图表、合并单元格等高级功能
缺点:
API相对复杂
处理大文件时内存消耗大(尤其是XSSF)
1.2.2 EasyExcel
EasyExcel是阿里巴巴开源的Excel处理库,基于POI,但做了大量优化。
优点:
内存占用低,使用SAX模式读取,避免OOM
API简单易用,注解驱动
读写速度快
适合处理大型Excel文件
缺点:
功能不如POI全面
灵活性相对较低
1.2.3 JExcel
JExcel是另一个处理Excel的Java库。
优点:
API较简单
速度较快
缺点:
仅支持旧版Excel (.xls)格式
不再积极维护
功能有限
1.2.4 Apache POI SXSSF
SXSSF是POI提供的一种流式处理模式,专为处理大型Excel文件设计。
优点:
大大降低内存占用
适合生成大型Excel文件
缺点:
仅支持写入操作,不支持读取
功能比XSSF受限
1.3 Spring Boot中集成Excel处理
Spring Boot本身不提供Excel处理功能,但可以轻松集成上述各种Excel处理库。本指南将主要介绍:
如何在Spring Boot项目中集成Apache POI和EasyExcel
如何实现Excel导入导出的常见功能
如何处理常见问题和优化性能
Spring Boot中Excel处理完全指南:从基础到高级实践
在数据驱动的现代应用中,Excel处理能力是后端开发的必备技能。本文全面解析Spring Boot中Excel操作的12种核心技术方案。
一、基础方案:Apache POI原生集成
1. 添加依赖
<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>5.2.3</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version>
</dependency>
2. 基础读写操作
// 写入Excel
public void writeExcel(List<User> users, String filePath) throws IOException {try (Workbook workbook = new XSSFWorkbook()) {Sheet sheet = workbook.createSheet("用户表");// 创建表头Row headerRow = sheet.createRow(0);headerRow.createCell(0).setCellValue("ID");headerRow.createCell(1).setCellValue("姓名");headerRow.createCell(2).setCellValue("年龄");// 填充数据int rowNum = 1;for (User user : users) {Row row = sheet.createRow(rowNum++);row.createCell(0).setCellValue(user.getId());row.createCell(1).setCellValue(user.getName());row.createCell(2).setCellValue(user.getAge());}// 自动调整列宽for (int i = 0; i < 3; i++) {sheet.autoSizeColumn(i);}try (FileOutputStream fos = new FileOutputStream(filePath)) {workbook.write(fos);}}
}// 读取Excel
public List<User> readExcel(String filePath) throws IOException {List<User> users = new ArrayList<>();try (Workbook workbook = new XSSFWorkbook(new File(filePath))) {Sheet sheet = workbook.getSheetAt(0);Iterator<Row> rowIterator = sheet.iterator();// 跳过表头if (rowIterator.hasNext()) rowIterator.next();while (rowIterator.hasNext()) {Row row = rowIterator.next();User user = new User();user.setId((long) row.getCell(0).getNumericCellValue());user.setName(row.getCell(1).getStringCellValue());user.setAge((int) row.getCell(2).getNumericCellValue());users.add(user);}}return users;
}
二、高效方案:EasyExcel处理百万级数据
1. 添加依赖
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.2</version>
</dependency>
2. 基于注解的实体映射
@Data
public class UserExcel {@ExcelProperty("ID")private Long id;@ExcelProperty("姓名")private String name;@ExcelProperty("年龄")private Integer age;@ExcelProperty(value = "创建时间", converter = LocalDateTimeConverter.class)private LocalDateTime createTime;
}// 自定义日期转换器
public class LocalDateTimeConverter implements Converter<LocalDateTime> {@Overridepublic LocalDateTime convertToJavaData(ReadCellData<?> cellData) {return LocalDateTime.parse(cellData.getStringValue(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));}
}
3. 百万数据导出(避免OOM)
@GetMapping("/exportBigData")
public void exportBigData(HttpServletResponse response) {response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("百万用户数据", "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");EasyExcel.write(response.getOutputStream(), UserExcel.class).sheet("用户数据").doWrite(() -> {// 分页查询数据(每次查询10000条)int page = 0;while (true) {Pageable pageable = PageRequest.of(page, 10000);Page<User> userPage = userRepository.findAll(pageable);if (userPage.isEmpty()) break;// 转换数据List<UserExcel> data = userPage.getContent().stream().map(user -> {UserExcel excel = new UserExcel();BeanUtils.copyProperties(user, excel);return excel;}).collect(Collectors.toList());page++;return data;}return null;});
}
三、Web集成方案:文件上传下载
1. 文件上传解析
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {if (file.isEmpty()) {return "请选择文件";}try {List<User> users = EasyExcel.read(file.getInputStream()).head(User.class).sheet().doReadSync();userRepository.saveAll(users);return "导入成功,共处理数据: " + users.size() + "条";} catch (IOException e) {return "文件处理失败: " + e.getMessage();}
}
2. 模板下载
@GetMapping("/downloadTemplate")
public void downloadTemplate(HttpServletResponse response) throws IOException {response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("用户导入模板", "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");// 创建模板数据(带下拉菜单)List<String> headers = Arrays.asList("ID", "姓名", "年龄", "部门");List<ExcelSelect> selects = Collections.singletonList(new ExcelSelect(3, Arrays.asList("研发部", "市场部", "财务部")));EasyExcel.write(response.getOutputStream()).head(headers).registerWriteHandler(new DropDownHandler(selects)) // 添加下拉菜单.sheet("模板").doWrite(Collections.emptyList());
}// 下拉菜单处理器
public class DropDownHandler implements SheetWriteHandler {private final List<ExcelSelect> selects;public DropDownHandler(List<ExcelSelect> selects) {this.selects = selects;}@Overridepublic void afterSheetCreate(WriteWorkbookContext context) {Sheet sheet = context.getWriteSheetHolder().getSheet();DataValidationHelper helper = sheet.getDataValidationHelper();for (ExcelSelect select : selects) {// 创建下拉数据源String range = "D2:D1048576"; // 示例:D列所有行CellRangeAddressList addressList = new CellRangeAddressList(1, 65535, select.getColIndex(), select.getColIndex());// 设置下拉选项DataValidationConstraint constraint = helper.createExplicitListConstraint(select.getOptions().toArray(new String[0]));DataValidation validation = helper.createValidation(constraint, addressList);// 应用验证sheet.addValidationData(validation);}}
}
四、高级技巧:动态模板与复杂报表
1. 动态表头生成
public void exportDynamicColumns(HttpServletResponse response, List<String> columns) {// 动态构建表头List<List<String>> head = new ArrayList<>();for (String col : columns) {head.add(Collections.singletonList(col));}// 动态数据填充List<List<Object>> data = new ArrayList<>();for (User user : userRepository.findAll()) {List<Object> row = new ArrayList<>();for (String col : columns) {switch (col) {case "ID": row.add(user.getId()); break;case "姓名": row.add(user.getName()); break;case "部门": row.add(user.getDepartment()); break;// 更多字段处理...}}data.add(row);}// 导出EasyExcel.write(response.getOutputStream()).head(head).sheet("动态报表").doWrite(data);
}
2. 复杂报表合并单元格
public void exportComplexReport() {String fileName = "复杂报表.xlsx";try (ExcelWriter excelWriter = EasyExcel.write(fileName).build()) {WriteSheet writeSheet = EasyExcel.writerSheet("部门统计").build();// 设置合并策略excelWriter.registerWriteHandler(new CellMergeStrategy(Arrays.asList(new MergeRange(0, 0, 0, 3), // 第一行合并4列new MergeRange(1, 1, 0, 0), // 第二行第一列new MergeRange(1, 1, 1, 3) // 第二行后三列合并)));// 构建表头List<List<String>> head = Arrays.asList(Collections.singletonList("公司年度报表"),Arrays.asList("部门", "Q1", "Q2", "Q3", "Q4"));// 填充数据List<List<Object>> data = new ArrayList<>();data.add(Arrays.asList("研发部", 150, 180, 200, 220));data.add(Arrays.asList("市场部", 120, 150, 180, 210));excelWriter.write(head, writeSheet);excelWriter.write(data, writeSheet);}
}// 自定义合并策略
public class CellMergeStrategy implements RowWriteHandler {private final List<MergeRange> mergeRanges;@Overridepublic void afterRowDispose(WriteSheetContext context) {Sheet sheet = context.getWriteSheetHolder().getSheet();for (MergeRange range : mergeRanges) {sheet.addMergedRegion(new CellRangeAddress(range.getFirstRow(), range.getLastRow(),range.getFirstCol(), range.getLastCol()));}}
}
五、性能优化与问题解决方案
1. 内存溢出解决方案
// 使用SXSSFWorkbook处理大数据
try (Workbook workbook = new SXSSFWorkbook(100)) { // 保持100行在内存中Sheet sheet = workbook.createSheet();// 写入数据for (int i = 0; i < 1000000; i++) {Row row = sheet.createRow(i);// 填充单元格...// 定期刷新磁盘if (i % 1000 == 0) {((SXSSFSheet) sheet).flushRows(100);}}
}
2. 常见问题处理方案
问题类型 | 解决方案 |
---|---|
日期格式不一致 | 自定义Converter统一处理日期格式 |
大文件导出超时 | 分页查询+异步导出,前端轮询结果 |
特殊字符(emoji)乱码 | 使用String sanitized = StringEscapeUtils.escapeHtml4(rawString) 处理 |
公式计算 | 使用FormulaEvaluator 预处理公式结果 |
样式丢失 | 定义CellStyle 缓存池避免重复创建 |
下拉菜单动态数据源 | 使用DVConstraint.createFormulaListConstraint 引用隐藏Sheet数据 |
3. Excel操作最佳实践
-
读写分离原则:
- 读操作:使用EasyExcel的监听器模式(
ReadListener
) - 写操作:大数据用分页写入,小数据用全量写入
- 读操作:使用EasyExcel的监听器模式(
-
资源管理三要素:
try (Workbook workbook = ...;InputStream is = ...) {// 操作逻辑 } // 自动关闭资源
-
样式复用技巧:
private CellStyle createHeaderStyle(Workbook workbook) {CellStyle style = workbook.createCellStyle();Font font = workbook.createFont();font.setBold(true);style.setFont(font);style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());style.setFillPattern(FillPatternType.SOLID_FOREGROUND);return style; }
六、扩展方案:集成第三方库
1. JExcelAPI(适合.xls格式)
<dependency><groupId>net.sourceforge.jexcelapi</groupId><artifactId>jxl</artifactId><version>2.6.12</version>
</dependency>
2. Apache POI + JXLS模板引擎
<dependency><groupId>org.jxls</groupId><artifactId>jxls-poi</artifactId><version>2.12.0</version>
</dependency>
<dependency><groupId>org.jxls</groupId><artifactId>jxls-jexcel</artifactId><version>1.0.9</version>
</dependency>
模板示例(template.xlsx):
| 姓名 | 年龄 | 部门 |
|------|------|----------|
| ${u.name} | ${u.age} | ${u.department} |
Java代码:
public void exportWithTemplate(HttpServletResponse response) throws IOException {try (InputStream is = getClass().getResourceAsStream("/template.xlsx");OutputStream os = response.getOutputStream()) {Context context = new Context();context.putVar("users", userRepository.findAll());JxlsHelper.getInstance().processTemplate(is, os, context);}
}
最佳实践总结:对于中小数据量(<10万行)推荐使用EasyExcel+注解方案;大数据场景采用POI的SXSSF模式;需要复杂报表时选择JXLS模板引擎。始终记住:在Spring Boot中处理Excel,资源关闭和内存管理是成败关键!
扩展学习:
- POI官方文档
- EasyExcel GitHub
- JXLS模板语法
相关文章:
Spring Boot中Excel处理完全指南:从基础到高级实践
Excel处理基础知识 1.1 为什么需要在应用中处理Excel文件? 在企业应用开发中,Excel文件处理是一个非常常见的需求,主要用于以下场景: 数据导入:允许用户通过Excel上传批量数据到系统 数据导出:将系统数据…...
Windows下NVM的安装与使用
本文将介绍windows下nvm相关知识。 在不同的项目中可能会使用不同版本的Node.js,例如A项目中需要node>18;B项目中需要node>20。这时候就需要使用NVM切换不同的node版本。进而可以在同一台设备上使用多个node版本。 一、NVM是什么? n…...
Ubuntu挂起和休眠
Ubuntu挂起和休眠 1. 挂起(Suspend)2. 休眠(Hibernate)3. 混合挂起(Hybrid-Sleep)注意事项图形界面操作 在 Ubuntu 系统中,挂起(Suspend)和休眠(Hibernate&am…...

【R语言编程绘图-mlbench】
mlbench库简介 mlbench是一个用于机器学习的R语言扩展包,主要用于提供经典的基准数据集和工具,常用于算法测试、教学演示或研究场景。该库包含多个知名数据集,涵盖分类、回归、聚类等任务。 包含的主要数据集 BostonHousing 波士顿房价数据…...
云服务器部署Gin+gorm 项目 demo
更多个人笔记见: (注意点击“继续”,而不是“发现新项目”) github个人笔记仓库 https://github.com/ZHLOVEYY/IT_note gitee 个人笔记仓库 https://gitee.com/harryhack/it_note 个人学习,学习过程中还会不断补充&…...
MySQL数据一致性守护者:pt-table-checksum原理与实战全解析
MySQL数据一致性守护者:pt-table-checksum原理与实战全解析 在MySQL主从复制环境中,数据一致性是DBA和运维人员最关心的问题之一。主从数据不一致可能导致业务逻辑错误、报表数据失真甚至系统故障。Percona Toolkit中的pt-table-checksum工具正是为解决这一痛点而生,它能够…...

检索器组件深入学习与使用技巧 BaseRetriever 检索器基类
1. BaseRetriever 检索器基类 在 LangChain 中,传递一段 query 并返回与这段文本相关联文档的组件被称为 检索器,并且 LangChain 为所有检索器设计了一个基类——BaseRetriever,该类继承了 RunnableSerializable,所以该类是一个 …...
Unity——QFramework工具 AciontKit时序动作执行系统
AciontKit 是一个时序动作执行系统。 游戏中,动画的播放、延时、资源的异步加载、网络请求等,这些全部都是时序任务,而 ActionKit,可以把这些任务全部整合在一起,使用统一的 API,来对他们的执行进行计划。…...

【Doris基础】Doris中的Replica详解:Replica原理、架构
目录 1 Replica基础概念 1.1 什么是Replica 1.2 Doris中的副本类型 2 Doris副本架构设计 2.1 副本分布机制 2.2 副本一致性模型 3 副本生命周期管理 3.1 副本创建流程 3.2 副本恢复机制 4 副本读写流程详解 4.1 写入流程与副本同步 4.2 查询流程与副本选择 5 副本…...

【中国·广州】第三届信号处理与智能计算国际学术会议 (SPIC2025) 即将开启
第三届信号处理与智能计算国际学术会议 (SPIC2025) 即将开启 在信息技术飞速发展的当下,信号处理与智能计算作为前沿科技领域,正深刻改变着我们的生活与产业格局。为汇聚全球顶尖智慧,推动该领域进一步突破,第三届信号处理与智能…...

Android12 Launcher3显示所有应用列表
Android12 Launcher3显示所有应用列表 1.前言: 最近在Android12Rom定制时需要显示所有桌面应用的图标,并且不能去掉抽屉,在手机上面抽屉和所有应该列表是两种不同模式,用户基可以自行选择,但是在自定义的launcher中这…...
24.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--认证微服务
SP.IdentityService 项目为微服务架构中的核心认证中心,采用 OpenIddict 框架实现 OAuth2.0 和 OpenID Connect 协议,提供完整的身份认证和授权解决方案。项目集成了 ASP.NET Core Identity 框架,实现了用户管理、角色权限控制等基础功能&…...
基于React Native开发鸿蒙新闻类应用的实战开发笔记
以下为基于React Native开发鸿蒙新闻资讯类应用的实战开发笔记,结合架构特性与踩坑经验,重点记录关键实现方案和技术决策: 一、环境搭建与工程初始化(关键步骤复盘) Node.js版本锁定 必须使用Node 18&…...
[Java 基础]运算符,将盒子套起来
在 Java 中,运算符(Operator)用于执行特定的操作,例如数学计算、赋值、比较等。运算符是 Java 语言的重要组成部分,能够帮助我们高效地操作数据。 1. 算术运算符 运算符说明示例结果加法5 38-减法5 - 32*乘法5 * 31…...

智能快递地址解析接口如何用PHP调用?
一、什么是智能快递地址解析接口 随着互联网技术的普及和电子商务的迅猛发展,网购已成为现代人日常生活的重要组成部分。然而,在这个便捷的背后,一个看似不起眼却影响深远的问题正悄然浮现——用户填写的快递地址格式混乱、信息不全甚至错漏…...

华为OD机试真题——模拟消息队列(2025B卷:100分)Java/python/JavaScript/C++/C语言/GO六种最佳实现
2025 B卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 2025华为OD真题目录+全流程解析/备考攻略/经验分享 华为OD机试真题《模拟消息队列》: 目录 题…...
c# 显示正在运行的线程数
在 C# 中,若想获取当前进程正在运行的线程数,可以使用 System.Diagnostics 命名空间中的 Process 类来实现。该方法适用于 Windows 平台,并能够获取当前进程的线程信息,包括线程总数和运行中的线程数量。 ✅ 方法一:使…...
MySQL 日志数据同步的详细教程
以下是 MySQL 日志数据同步的详细教程,主要介绍基于二进制日志(binlog)的主从复制和基于 GTID 的高级同步方案: 一、MySQL 二进制日志(binlog)同步基础 1. 二进制日志原理 binlog 是 MySQL 的事务性日志&am…...
2025 Java面试大全技术文章(面试题1)
数据类型与包装类 问题:Java中基本数据类型与包装类的区别是什么?自动装箱与拆箱的底层原理? 答案: 基本数据类型(如int、double)直接存储值,包装类(如Integer、Double)…...
docker 中 什么是「卷」?(Volume)
🗃️ 什么是「卷」?(Volume) 「卷」就是 Docker 里用来“保存数据”的一块空间,就像是一个外接硬盘,或者一个 USB 闪存。 容器本身是临时的,你一删它,它的数据也跟着没了。但卷是用…...
三维可视化和实时数据处理对前端性能要求以及优化渲染效率
在三维可视化(如 Three.js 场景)和实时数据处理(如每秒数百条设备状态更新)场景中,前端性能优化是确保用户体验的核心挑战。以下结合技术原理与行业实践,详细说明Web Workers和虚拟 DOM的优化机制ÿ…...

基于VU37P的高性能采集板卡
基于VU37P的高性能采集板卡是一款最大可提供20路ADC接收通道的高性能采集板卡。每路A/D通道支持1GS/s的采样率,分辨率为14bit,模拟输入带宽可达500MHz,交流耦合,输入阻抗50欧姆。 产品简介 可提供20路ADC接收通道的高性能采集板…...

2025-05-31 Python深度学习10——模型训练流程
文章目录 1 数据准备1.1 下载与预处理1.2 数据加载 2 模型构建2.1 自定义 CNN 模型2.2 GPU加速 3 训练配置3.1 损失函数3.2 优化器3.3 训练参数 4 训练循环4.1 训练模式 (model.train())4.2 评估模式 (model.eval()) 5 模型验证 本文环境: Pycharm 2025.1Python 3.1…...
卷积神经网络(CNN)、YOLO和人脸识别之间的关系
核心关系图解 TEXT 摄像头图像 → [YOLO:人脸检测] → 定位人脸位置 → [CNN:特征提取] → 人脸特征向量 → [人脸识别系统] → 身份匹配 通俗比喻 想象你在一个拥挤的火车站找人: YOLO 是你的"快速扫描眼": 一眼扫…...

K8S StatefulSet 快速开始
其实这篇文章的梗概已经写了很久了,中间我小孩出生了,从此人间多了一份牵挂。抽出一些时间去办理新生儿相关手续。初为人父确实艰辛,就像学技术一样,都需要有极大的耐心,付出很多的时间。 一、引子 1.1、独立的存储 …...
重新测试deepseek Jakarta EE 10编程能力
听说deepseek做了一个小更新,我重新测试了一下Jakarta EE 10编程能力;有点进步,遗漏的功能比以前少了。 采用Jakarta EE 10 编写员工信息表维护表,包括员工查询与搜索、员工列表、新增员工、删除员工,修改员工…...

nav2笔记-250603
合作背景: AMD与Open Navigation在过去几个月里进行了合作,旨在向ROS 2社区展示AMD强大的Ryzen AI、Embedded和Kria能力。 演示内容: 帖子提到,他们已经开始展示如何使用Ryzen AI为自主机器人产品提供动力,在各种现实世…...

指纹识别+精准化POC攻击
开发目的 解决漏洞扫描器的痛点 第一就是扫描量太大,对一个站点扫描了大量的无用 POC,浪费时间 指纹识别后还需要根据对应的指纹去进行 payload 扫描,非常的麻烦 开发思路 我们的思路分为大体分为指纹POC扫描 所以思路大概从这几个方面…...
LeetCode[404]左叶子之和
思路: 题目要求求出左叶子的和,左叶子的条件是左右节点为空且是左子树的叶子节点才叫左叶子节点,那么右子树的左叶子节点的和是什么呢?这样想就引出了递归的顺序,后序遍历,求出左右子树的节点和,…...

mac环境下的python、pycharm和pip安装使用
Python安装 Mac环境下的python安装 下载地址:https://www.jetbrains.com.cn/pycharm/ 一直点击下一步即可完成 在应用程序中会多了两个图标 IDLE 和 Python launcher IDLE支持在窗口中直接敲python命令并立即执行,双击即可打开 Python launcher双击打…...