Spring Boot集成EasyExcel
1. 初始化Spring Boot项目
首先,使用Spring Initializr(https://start.spring.io/)生成一个基本的Spring Boot项目。选择以下依赖项:
- Spring Web
- Lombok (用于减少样板代码)
- SLF4J (用于日志记录)
2. 添加依赖
在你的pom.xml文件中添加EasyExcel的Maven依赖。确保版本号是最新的,你可以访问Maven仓库来获取最新版。
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>最新的版本号</version>
</dependency>
3. 创建实体类
假设我们需要处理一个用户信息表,包含姓名和年龄两个字段。以下是实体类的设计,并使用Lombok简化代码:
package com.example.demo.model;import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;// 使用@Data注解自动生成getter、setter等方法
@Data
public class UserData {// 指定Excel列标题为“姓名”@ExcelProperty("姓名")private String name; // 用户姓名// 指定Excel列标题为“年龄”@ExcelProperty("年龄")private Integer age; // 用户年龄
}
4. 创建监听器类
创建一个监听器类来处理每一行的数据,并在服务类中调用它。我们使用@Slf4j注解简化日志记录:
package com.example.demo.listener;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.demo.model.UserData;
import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;
import java.util.List;@Slf4j // 使用@Slf4j注解简化日志记录
public class UserDataListener extends AnalysisEventListener<UserData> {private final List<UserData> dataList = new ArrayList<>(); // 存储读取的数据/*** 当解析一行数据时调用* @param userData 解析得到的用户数据* @param analysisContext 上下文对象*/@Overridepublic void invoke(UserData userData, AnalysisContext analysisContext) {dataList.add(userData); // 将每一行的数据添加到列表中log.info("读取到一条数据: {} {}", userData.getName(), userData.getAge()); // 日志记录}/*** 所有数据解析完成后调用* @param analysisContext 上下文对象*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {log.info("所有数据解析完成"); // 数据解析完成后记录日志}/*** 获取读取的数据列表* @return 读取的数据列表*/public List<UserData> getDataList() {return dataList; // 返回读取的数据列表}
}
5. 实现服务类
编写一个服务类来实现数据的读写操作,并增加异常处理和日志记录。我们将在此处添加分页功能,以确保当单个页面数据达到一定量时重新生成新的页面进行写入:
package com.example.demo.service;import com.alibaba.excel.EasyExcel;
import com.example.demo.listener.UserDataListener;
import com.example.demo.model.UserData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Slf4j // 使用@Slf4j注解简化日志记录
@Service
public class ExcelService {private static final int PAGE_SIZE = 100; // 每页最大数据条数/*** 写入Excel文件* @param filePath 文件路径* @param data 要写入的数据列表*/public void writeExcel(String filePath, List<UserData> data) {try {log.info("开始写入Excel文件: {}", filePath); // 记录日志int totalRows = data.size();int totalPages = (int) Math.ceil((double) totalRows / PAGE_SIZE);for (int page = 0; page < totalPages; page++) {int fromIndex = page * PAGE_SIZE;int toIndex = Math.min(fromIndex + PAGE_SIZE, totalRows);List<UserData> currentPageData = data.subList(fromIndex, toIndex);String sheetName = "用户信息" + (page + 1); // 设置工作表名称// 开始写入Excel文件EasyExcel.write(filePath, UserData.class).sheet(sheetName) // 设置工作表名称.doWrite(currentPageData); // 执行写入操作log.info("写入第 {} 页数据完成", page + 1); // 写入完成后记录日志}log.info("写入Excel文件完成"); // 写入完成后记录日志} catch (Exception e) {log.error("写入Excel失败", e); // 记录错误日志}}/*** 读取所有工作表的Excel文件* @param inputStream 输入流* @return 读取的数据列表*/public List<UserData> readAllSheets(InputStream inputStream) {List<UserData> allData = new ArrayList<>();try {log.info("开始读取所有工作表的Excel文件"); // 记录日志// 获取Excel文件中的所有Sheet信息List<String> sheetNames = EasyExcel.read(inputStream).excelExecutor().sheetList().get();for (String sheetName : sheetNames) {log.info("开始读取工作表: {}", sheetName);UserDataListener listener = new UserDataListener();// 执行读取操作EasyExcel.read(inputStream, UserData.class, listener).sheet(sheetName) // 指定工作表名称.doRead(); // 执行读取操作// 处理listener.getDataList()allData.addAll(listener.getDataList());log.info("读取工作表 {} 完成", sheetName);}log.info("读取所有工作表的Excel文件完成"); // 读取完成后记录日志} catch (Exception e) {log.error("读取所有工作表的Excel失败", e); // 记录错误日志}return allData;}/*** 读取特定工作表的Excel文件* @param filePath 文件路径* @param sheetIndex 工作表索引(从0开始)*/public void readSpecificSheet(String filePath, int sheetIndex) {try {log.info("开始读取特定工作表的Excel文件: {}, Sheet Index: {}", filePath, sheetIndex); // 记录日志UserDataListener listener = new UserDataListener();// 执行读取操作EasyExcel.read(filePath, UserData.class, listener).sheet(sheetIndex) // 指定工作表索引.doRead(); // 执行读取操作// 处理listener.getDataList()for (UserData userData : listener.getDataList()) {log.info("{} {}", userData.getName(), userData.getAge()); // 记录每条数据的日志}log.info("读取特定工作表的Excel文件完成"); // 读取完成后记录日志} catch (Exception e) {log.error("读取特定工作表的Excel失败", e); // 记录错误日志}}/*** 使用模板填充数据并生成新的Excel文件* @param templateFilePath 模板文件路径* @param outputFilePath 输出文件路径* @param data 要填充的数据列表*/public void fillTemplate(String templateFilePath, String outputFilePath, List<UserData> data) {try {log.info("开始使用模板填充数据: {}, 输出文件路径: {}", templateFilePath, outputFilePath); // 记录日志// 使用模板填充数据EasyExcel.write(outputFilePath).withTemplate(templateFilePath).sheet().doFill(data);log.info("模板填充数据完成"); // 填充完成后记录日志} catch (Exception e) {log.error("模板填充数据失败", e); // 记录错误日志}}/*** 下载文件并在失败时返回JSON* @param response HttpServletResponse 对象* @throws IOException 如果写入文件失败*/public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {try {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");List<UserData> data = new ArrayList<>();for (int i = 1; i <= 100; i++) { // 假设我们有100条数据data.add(new UserData().setName("张三" + i).setAge(20 + i % 50));}EasyExcel.write(response.getOutputStream(), UserData.class).autoCloseStream(Boolean.FALSE).sheet("模板").doWrite(data);} catch (Exception e) {// 重置responseresponse.reset();response.setContentType("application/json");response.setCharacterEncoding("utf-8");Map<String, String> map = Map.of("status", "failure", "message", "下载文件失败: " + e.getMessage());response.getWriter().println(JSON.toJSONString(map));}}
}
}
6. 创建控制器类
为了方便测试,我们可以创建一个简单的控制器类来调用服务类中的方法:
package com.example.demo.controller;import com.example.demo.service.ExcelService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@Slf4j // 使用@Slf4j注解简化日志记录
@RestController
@RequestMapping("/excel")
public class ExcelController {@Autowiredprivate ExcelService excelService; // 自动注入ExcelService/*** 文件上传* @param file 上传的文件* @return 成功消息* @throws IOException 如果读取文件失败*/@PostMapping("upload")@ResponseBodypublic String upload(@RequestParam("file") MultipartFile file) throws IOException {InputStream inputStream = file.getInputStream();List<UserData> data = excelService.readAllSheets(inputStream);log.info("成功读取到 {} 条数据", data.size());return "success";}/*** 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)** @param response HttpServletResponse 对象* @throws IOException 如果写入文件失败*/@GetMapping("downloadFailedUsingJson")public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {excelService.downloadFailedUsingJson(response);}/*** 写入Excel文件* @param filePath 文件路径* @return 成功消息*/@GetMapping("/write")public String writeExcel(@RequestParam String filePath) {log.info("准备写入Excel文件: {}", filePath); // 记录日志List<UserData> data = new ArrayList<>();for (int i = 1; i <= 500; i++) { // 假设我们有500条数据data.add(new UserData().setName("张三" + i).setAge(20 + i % 50));}excelService.writeExcel(filePath, data); // 调用写入方法log.info("写入Excel文件完成"); // 返回成功消息return "写入Excel成功"; // 返回成功消息}/*** 读取所有工作表的Excel文件* @param filePath 文件路径* @return 成功消息*/@GetMapping("/read/allSheets")public String readAllSheets(@RequestParam String filePath) {log.info("准备读取所有工作表的Excel文件: {}", filePath); // 记录日志excelService.readAllSheets(filePath); // 调用读取方法log.info("读取所有工作表的Excel文件完成"); // 返回成功消息return "读取所有工作表的Excel成功"; // 返回成功消息}/*** 读取特定工作表的Excel文件* @param filePath 文件路径* @param sheetIndex 工作表索引(从0开始)* @return 成功消息*/@GetMapping("/read/specificSheet")public String readSpecificSheet(@RequestParam String filePath, @RequestParam int sheetIndex) {log.info("准备读取特定工作表的Excel文件: {}, Sheet Index: {}", filePath, sheetIndex); // 记录日志excelService.readSpecificSheet(filePath, sheetIndex); // 调用读取方法log.info("读取特定工作表的Excel文件完成"); // 返回成功消息return "读取特定工作表的Excel成功"; // 返回成功消息}/*** 使用模板填充数据并生成新的Excel文件* @param templateFilePath 模板文件路径* @param outputFilePath 输出文件路径* @return 成功消息*/@GetMapping("/fill/template")public String fillTemplate(@RequestParam String templateFilePath, @RequestParam String outputFilePath) {log.info("准备使用模板填充数据: {}, 输出文件路径: {}", templateFilePath, outputFilePath); // 记录日志List<UserData> data = new ArrayList<>();for (int i = 1; i <= 500; i++) { // 假设我们有500条数据data.add(new UserData().setName("张三" + i).setAge(20 + i % 50));}excelService.fillTemplate(templateFilePath, outputFilePath, data); // 调用模板填充方法log.info("模板填充数据完成"); // 返回成功消息return "模板填充数据成功"; // 返回成功消息}
}
7. 异常处理与日志记录
在实际应用中,建议增加更多的异常处理逻辑和日志记录,以便更好地调试和维护。我们已经在服务类中添加了基本的日志记录和异常处理机制。
全局异常处理
你可以在项目中添加全局异常处理器来捕获和处理未处理的异常:
package com.example.demo.exception;import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import lombok.extern.slf4j.Slf4j;@Slf4j // 使用@Slf4j注解简化日志记录
@ControllerAdvice
public class GlobalExceptionHandler {/*** 处理所有异常* @param e 异常对象* @return 错误响应*/@ExceptionHandler(Exception.class)public ResponseEntity<String> handleException(Exception e) {log.error("发生错误: ", e); // 记录错误日志return new ResponseEntity<>("发生错误: " + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); // 返回错误消息}
}
8. 启动应用程序
确保你的主应用程序类正确配置:
package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import lombok.extern.slf4j.Slf4j;@SpringBootApplication
@Slf4j // 使用@Slf4j注解简化日志记录
public class DemoApplication {/*** 主函数,启动Spring Boot应用* @param args 命令行参数*/public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args); // 启动Spring Boot应用log.info("Spring Boot应用已启动"); // 记录启动日志}
}
9. 测试
启动应用程序后,你可以通过浏览器或Postman等工具访问以下URL来测试Excel的读写功能:
- 写入Excel:
http://localhost:8080/excel/write?filePath=/path/to/your/output.xlsx - 读取Excel:
http://localhost:8080/excel/read?filePath=/path/to/your/input.xlsx
总结
以上是完整的Spring Boot集成EasyExcel的详细步骤和代码示例,包括详细的注释以及实现了当单个页面数据达到一定量时重新生成新的页面进行写入的功能。
相关文章:
Spring Boot集成EasyExcel
1. 初始化Spring Boot项目 首先,使用Spring Initializr(https://start.spring.io/)生成一个基本的Spring Boot项目。选择以下依赖项: Spring WebLombok (用于减少样板代码)SLF4J (用于日志记录) 2. 添加依赖 在你的pom.xml文件…...
obeaver 连接oracle 库 模式乱码
下载orai18n-12.1.0.2.0.jar 库--添加文件--把提前下载好的jar 随便放在一个文件夹下--添加文件选中,然后点击找到类, 选择类,确定即可正常 下载地址:https://download.csdn.net/download/weixin_42845364/88368302...
ChatGPT 使用教程:深度探索AI常用功能技巧
文章目录 前言一、ChatGPT介绍1.1 人工智能与自然语言处理的发展1.2 ChatGPT 的诞生与意义 二、ChatGPT 基础入门2.1 注册与登录2.2 对话界面介绍2.3 基本提问方式 三、常用功能详解3.1 文本生成3.2 问题回答3.3 语言翻译3.4 代码生成与调试 四、高级使用技巧4.1 指令优化4.2 多…...
無人機的應用程序有那些可以部署在linux server 系統
Dronecode Project:由 Linux Foundation 主導的開源項目,提供無人機航空操作系統和導航工具的開發框架,適合開發者使用。 DeepSeek-R1:這是一個人工智能模型,適用於無人機的數據處理和分析,支持在 Linux 系…...
[HUBUCTF 2022 新生赛]messy_traffic
下载附件 看到文件类型直接用wireshark打开,对MySQL协议进行追踪流,并没有什么发现,后面对NO.437发现有用信息,http追踪流 发现**system(‘cat passwd.txt’);**这里是在打开查看passwd.txt,密码是"SignUpForHUBU…...
铁人三项(第五赛区)_2018_rop题解
先启动靶机连接看看。 直接ls,就给我输出句话,看来不能直接拿flag。 那走下流程。 查下位数和其他信息: 可以看到是32位的包,开了NX,但没开其他保护。 用ida32打开looklook。 主函数就是个这,看到了弹出的…...
package.json 依赖包约束及快速删除node_modules
文章目录 一、package.json版本约束1、初始项目安装2. 已有 yarn.lock 文件的项目安装3. 特殊情况手动修改 package.json 版本:使用 yarn upgrade 命令: 二、快速删除node_modules三、depcheck 检测npm未使用的依赖 一、package.json版本约束 1、初始项…...
Compose 实践与探索六 —— 动画的流程控制与 Transition
1、Block 参数:监听每一帧 animateTo() 与 animateDecay() 中都有一个函数类型的 block 参数: suspend fun animateDecay(initialVelocity: T,animationSpec: DecayAnimationSpec<T>,block: (Animatable<T, V>.() -> Unit)? null): An…...
虚拟机Contos7为啥不能被本机电脑访问?
1.查看防火墙是否开启 systemctl status firewalld.service 2.如果防火墙关闭就可以直接被访问 3.如果防火墙打开了我们需要开放端口(下面为防火墙一系列指令) # 关闭防火墙 systemctl stop firewalld.service# 打开防火墙 systemctl start firewalld.service# 关闭开启自启…...
【21】单片机编程核心技巧:if语句逻辑与真假判断
【21】单片机编程核心技巧:if语句逻辑与真假判断 七律 条件分野 if语句判真假,括号条件定乾坤。 非零为真零为假,大括号内藏玄门。 省略虽简风险在,代码规范护本根。 单片逻辑由心控,条件分支自成文。 注释…...
Java 实现 Android ViewPager2 顶部导航:动态配置与高效加载指南
Java 实现:明确使用的编程语言。Android ViewPager2:技术栈和核心组件。顶部导航:功能点。动态配置与高效加载指南:突出动态配置的灵活性和性能优化的重点。 在 Android 中使用 Java 实现 ViewPager2 和 TabLayout 的顶部导航也是…...
Python :数据模型
一. 什么是数据模型? Python数据模型是Python对象系统的抽象,通过一组特殊方法(如__init__、__len__等)和协议(如迭代协议、上下文管理协议),定义了对象如何与语言的内置功能(如…...
idea超级AI插件,让 AI 为 Java 工程师
引言 用户可在界面中直接通过输入自然语言的形式描述接口的需求,系统通过输入的需求自动分析关键的功能点有哪些,并对不确定方案的需求提供多种选择,以及对需求上下文进行补充,用户修改确定需求后,系统会根据需求设…...
施磊老师c++笔记(五)
继承与多态-深入掌握oop语言最强大的机制 文章目录 继承与多态-深入掌握oop语言最强大的机制1.继承的基本意义2.派生类的构造过程3.重载,隐藏,覆盖4.虚函数, 静态绑定和动态绑定--面试重点5.虚析构函数--重点在于什么呢时候用6.再讨论虚函数和动态绑定7.理解多态到底是什么8.理…...
µCOS-III从入门到精通 第十四章(软件定时器)
参考教程:【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili 一、软件定时器简介 1、定时器的概念与种类 (1)定时器的概念:从指定的时刻开始,经过一个指定时间,然后触发一个超时事件&…...
MySQL数据库复杂的增删改查操作
在前面的文章中,我们主要学习了数据库的基础知识以及基本的增删改查的操作。接下去将以一个比较实际的公司数据库为例子,进行讲解一些较为复杂且现时需求的例子。 基础知识: 一文清晰梳理Mysql 数据库基础知识_字段变动如何梳理清楚-CSDN博…...
KCD 北京站丨Volcano 邀您畅聊云原生智能调度技术与应用
AI与云原生技术正以前所未有的速度改变着我们的世界,而云原生技术则如同一座坚实的桥梁,连接着传统IT与现代化的数字世界。当AI与云原生相遇,它们相互赋能,相互促进,为开发者们打开了一个全新的技术宇宙。 3 月 15 日&…...
BLEU评估指标
一、介绍 用于评估模型生成的句子和实际句子差异的指标,取值在[0,1],匹配度高就距离1近,反之距离0近。这个指标计算代价小,容易理解,与语言无关,与人类评价结果高度相关。 BLEU主要基于n-gram匹配&#x…...
高效自动化测试:打造Python+Requests+Pytest+Allure+YAML的接口测试框架
一、背景 在快节奏的开发周期中,如何确保接口质量?自动化测试是关键。通过构建标准化、可复用的测试框架,能显著提升测试效率与准确性,为项目质量保驾护航[1][7]。 二、目标 ✅ 核心目标: ● 实现快速、高效的接口测试…...
如何修复 Tauri 发布后程序运行时显示 `asset not found: index.html` 的问题
如何修复 Tauri 发布后程序运行时显示 asset not found: index.html 的问题 在使用 Tauri 发布应用程序时,如果运行时出现 asset not found: index.html 的错误,通常是因为 Tauri 无法找到或正确加载前端资源文件(如 index.html)…...
BSides Vancouver: 2018 (Workshop)
BSides Vancouver: 2018 (Workshop) 来自 <https://www.vulnhub.com/entry/bsides-vancouver-2018-workshop,231/> 1,将两台虚拟机网络连接都改为NAT模式 2,攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23…...
rStar论文精读
论文简介 论文标题:《Mutual reasoning makes smaller LLMs stronger problem-solvers》 论文地址:https://arxiv.org/abs/2408.06195 录用会议:ICLR2025 背景与挑战 挑战1:在SLM中平衡exploration与exploitation。一些方法有很…...
【动态规划】对局匹配 (分组线性DP)
题目详情 问题描述: 小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。 小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起。如果两人分差小于或大于K…...
python 提取视频中的音频
在Python中提取视频中的音频,你可以使用moviepy库,这是一个非常强大且易于使用的库,专门用于视频编辑。以下是如何使用moviepy来提取视频中的音频的步骤: 安装moviepy 首先,你需要安装moviepy。你可以通过pip安装它&a…...
self.cls_token在 Vision Transformer (ViT) 模型中的训练阶段和推理阶段的行为和作用的异同
self.cls_token 在 Vision Transformer (ViT) 模型中,在训练阶段和推理阶段的行为和作用是不同的,而且它的值在训练过程中会发生变化。 1. self.cls_token 的作用 在 ViT 中,self.cls_token 是一个特殊的、可学习的嵌入向量(emb…...
【量化科普】Leverage,杠杆
【量化科普】Leverage,杠杆 🚀量化软件开通 🚀量化实战教程 在量化投资领域,杠杆(Leverage)是一个核心概念,它允许投资者通过借入资金来增加投资规模,从而放大投资收益或亏损。简…...
247g 的工业级电调,如何让无人机飞得更 “聪明“?——STONE 200A-M 深度测评
一、轻量化设计背后的技术取舍 当拿到 STONE 200A-M 时,247g 的重量让人意外 —— 这个接近传统 200A 电调 70% 的重量,源自 1205624.5mm 的紧凑结构(0.1mm 公差控制)。实测装机显示,相比同规格产品,其体积…...
Maven Deploy Plugin如何使用?
在Java开发中,Maven是一个非常重要的构建工具。它不仅可以管理项目的依赖关系,还能帮助我们打包和发布项目。在Maven中,deploy插件是一个很实用的功能,它可以将构建好的项目发布到远程仓库。今天,就来聊聊如何使用Mave…...
Node.js:快速启动你的第一个Web服务器
Node.js 全面入门指南 文章目录 Node.js 全面入门指南一 安装Node.js1. Windows2. MacOS/Linux 二 配置开发环境1. VSCode集成 三 第一个Node.js程序1. 创建你的第一个Node.js程序 四 使用Express框架1. 快速搭建服务器 一 安装Node.js 1. Windows 以下是Windows环境下Node.j…...
自定义日志回调函数实现第三方库日志集成:从理论到实战
一、应用场景与痛点分析 在开发过程中,我们经常会遇到以下场景: 日志格式统一:第三方库使用自己的日志格式,导致系统日志混杂,难以统一管理和分析。日志分级过滤:需要动态调整第三方库的日志输出级别&…...
