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操作最佳实践

  1. 读写分离原则

    • 读操作:使用EasyExcel的监听器模式(ReadListener
    • 写操作:大数据用分页写入,小数据用全量写入
  2. 资源管理三要素

    try (Workbook workbook = ...;InputStream is = ...) {// 操作逻辑
    } // 自动关闭资源
    
  3. 样式复用技巧

    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模板语法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/908003.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python编程基础(四) | if语句

引言&#xff1a;很久没有写 Python 了&#xff0c;有一点生疏。这是学习《Python 编程&#xff1a;从入门到实践&#xff08;第3版&#xff09;》的课后练习记录&#xff0c;主要目的是快速回顾基础知识。 练习1&#xff1a;条件测试 编写一系列条件测试&#xff0c;将每个条…

使用pandas实现合并具有共同列的两个EXCEL表

表1&#xff1a; 表2&#xff1a; 表1和表2&#xff0c;有共同的列“名称”&#xff0c;而且&#xff0c;表1的内容&#xff08;行数&#xff09;<表2的行数。 目的&#xff0c;根据“名称”列的对应内容&#xff0c;将表2列中的“所处行业”填写到表1相应的位置。 实现代…

ERP学习-AP

业务需要。持续更新学习进度 借助网上零搭建平台上手实操 这个是简道云平台页面链接&#xff0c;登录的化去手机号登录 目前开始对应付模块进行学习

Dify知识库下载小程序

一、Dify配置 1.查看或创建知识库的API 二、下载程序配置 1. 安装依赖resquirements.txt ######requirements.txt##### flask2.3.3 psycopg2-binary2.9.9 requests2.31.0 python-dotenv1.0.0#####安装依赖 pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.…

【PbstarAdmin】微前端架构下的高效后台管理系统解决方案

如果你正在寻找一个高效、稳定、易于使用、易于扩展的管理后台解决方案&#xff0c;PbstarAdmin 绝对值得一试。以下是它的在线演示和官方文档地址&#xff0c;你可以先睹为快&#xff1a; 在线演示&#xff1a;http://pbstar-admin.pbstar.cn/官方文档&#xff1a;http://pbs…

Java基础之数组(附带Comparator)

文章目录 基础概念可变参数组数组与ListComparator类1,基本概念2,使用Comparator的静态方法&#xff08;Java 8&#xff09;3,常用Comparator方法4,例子 排序与查找数组复制其他 基础概念 int[] anArray new int[10];只有创建对象时才会使用new关键字&#xff0c;所以数组是个…

Apache Doris 在数据仓库中的作用与应用实践

在当今数字化时代&#xff0c;企业数据呈爆炸式增长&#xff0c;数据仓库作为企业数据管理和分析的核心基础设施&#xff0c;其重要性不言而喻。而 Apache Doris&#xff0c;作为一款基于 MPP&#xff08;Massively Parallel Processing&#xff0c;大规模并行处理&#xff09;…

P1438 无聊的数列/P1253 扶苏的问题

因为这两天在写线性代数的作业&#xff0c;没怎么写题…… P1438 无聊的数列 题目背景 无聊的 YYB 总喜欢搞出一些正常人无法搞出的东西。有一天&#xff0c;无聊的 YYB 想出了一道无聊的题&#xff1a;无聊的数列。。。 题目描述 维护一个数列 ai​&#xff0c;支持两种操…

SpringBoot 自定义注解实现限流

SpringBoot 自定义注解实现限流 限流是为了防止服务器资源的过度消耗&#xff0c;通过一定的策略来控制访问频率&#xff0c;确保服务的高可用性和稳定性。其核心意义在于防止流量高峰时期接口过载&#xff0c;从而引起服务崩溃或响应延迟增加。本文将简述如何通过AOP和自定义…

Unity——QFramework框架 内置工具

QFramework 除了提供了一套架构之外&#xff0c;QFramework 还提供了可以脱离架构使用的工具 TypeEventSystem、EasyEvent、BindableProperty、IOCContainer。 这些工具并不是有意提供&#xff0c;而是 QFramework 的架构在设计之初是通过这几个工具组合使用而成的。 内置工具…

Vue3.5 企业级管理系统实战(二十二):动态菜单

在前几篇内容中已完成菜单、角色及菜单权限等相关开发&#xff0c;若要在左侧菜单根据用户角色动态展示菜单&#xff0c;需对 Sidebar 中的相关数据进行修改。鉴于其他相关方法及类型已在前文实现&#xff0c;本文不再重复阐述。 1 修改 Sidebar 组件 在 src/layout/componen…

014校园管理系统技术解析:构建智慧校园管理平台

校园管理系统技术解析&#xff1a;构建智慧校园管理平台 在教育信息化快速发展的当下&#xff0c;校园管理系统成为提升学校管理效率、优化校园服务的重要工具。该系统集成院校管理、投票管理等多个核心模块&#xff0c;面向管理员、用户和院内管理员三种角色&#xff0c;通过…

创新农业社会化服务 中和农信服务小农户的探索实践

在实现乡村振兴的道路上&#xff0c;如何让现代农业发展成果惠及广大小农户&#xff0c;是一个重要课题。作为国内领先的综合助农机构&#xff0c;中和农信多年来深耕农村市场&#xff0c;在服务小农户方面进行了诸多创新探索&#xff0c;走出了一条具有示范意义的农业社会化服…

6.3 day 35

知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观推理的写法&#xff1a;评估模式 可视化 理解深度学习网络最重要的2点&#xff1a; 1.了解损失如何定…

【如何在IntelliJ IDEA中新建Spring Boot项目(基于JDK 21 + Maven)】

AA. 我的开发环境配置与核心工具链解析 一、开发环境全览 C:\Users\Again>java -version java version "21.0.1" 2023-10-17 LTS Java(TM) SE Runtime Environment (build 21.0.112-LTS-29) Java HotSpot(TM) 64-Bit Server VM (build 21.0.112-LTS-29, mixed m…

【C++高级主题】多重继承下的类作用域

目录 一、类作用域与名字查找规则&#xff1a;理解二义性的根源 1.1 类作用域的基本概念 1.2 单继承的名字查找流程 1.3 多重继承的名字查找特殊性 1.4 关键规则&#xff1a;“最近” 作用域优先&#xff0c;但多重继承无 “最近” 二、多重继承二义性的典型类型与代码示…

登录vmware vcenter报vSphere Client service has stopped working错误

一、问题 登录vmware vcenter时发现报vSphere Client service has stopped working错误&#xff0c;导致vcenter控制台进不去 二、解决办法 打开vmware vcenter管理https://vcenterIP:5480&#xff0c;选择VMware vSphere Client&#xff0c;重启该服务后恢复正常。

MySQL关系型数据库学习

学习参考链接&#xff1a;https://www.runoob.com/mysql/mysql-tutorial.html Windows 安装MYSQL服务端的步骤&#xff1a;https://www.runoob.com/w3cnote/windows10-mysql-installer.html 1. 概念学习 MySQL 是一种关联数据库管理系统&#xff0c;关联数据库将数据保存在不…

web攻防之SSTI 注入漏洞

知识简介 &#xff1a; 模版引擎和框架的区别 ssti的中文翻译 &#xff1a; 服务端的模版的注入 模版引擎 &#xff1a;前端的用于装饰优化html的模版 最简单的就是在腾讯会议中的聊天功能 框架 &#xff1a; 这个是一套独立存在的逻辑 如TP他是一个区别于php语法的后端逻辑…

【清晰教程】利用Git工具将本地项目push上传至GitHub仓库中

Git 是一个分布式版本控制系统&#xff0c;由 Linus Torvalds 创建&#xff0c;用于有效、高速地处理从小到大的项目版本管理。GitHub 是一个基于 Git 的代码托管平台&#xff0c;提供了额外的协作和社交功能&#xff0c;使项目管理更加高效。它们为项目代码管理、团队协作和持…