SpringBoot实战:Excel文件上传、数据验证与存储全流程解析

一、需求场景与技术选型

在企业管理、数据中台等系统中,Excel文件处理是常见需求。本文将基于SpringBoot实现以下核心功能:

  1. 支持.xls/.xlsx文件上传
  2. 数据完整性验证(非空、格式等)
  3. 业务数据验证(关联数据库校验)
  4. 异常数据记录与反馈
  5. 数据批量入库

技术栈

  • SpringBoot 2.7+
  • Apache POI + EasyExcel
  • MyBatis-Plus 3.5+
  • H2 Database(演示用)

二、环境准备

2.1 添加依赖

<!-- Web支持 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><!-- Excel处理 -->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version>
</dependency><!-- 数据库 -->
<dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency><!-- 工具类 -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>

2.2 配置文件

server:port: 8080servlet:context-path: /excel-demospring:datasource:driver-class-name: org.h2.Driverurl: jdbc:h2:mem:testdbusername: sapassword:servlet:multipart:max-file-size: 50MBmax-request-size: 100MBmybatis-plus:configuration:map-underscore-to-camel-case: true

三、核心实现

3.1 文件上传接口

@RestController
@RequestMapping("/api/excel")
@Slf4j
public class ExcelImportController {@PostMapping("/upload")public ResponseEntity<Map<String, Object>> uploadExcel(@RequestParam("file") MultipartFile file) {try {String originalFilename = file.getOriginalFilename();if (!originalFilename.matches("^.+\\.(?i)(xls|xlsx)$")) {return ResponseEntity.badRequest().body(Collections.singletonMap("error", "Invalid file format"));}String savePath = "/tmp/uploads/";File dest = new File(savePath + originalFilename);if (!dest.getParentFile().exists()) {dest.getParentFile().mkdirs();}file.transferTo(dest);// 调用处理服务ImportResult result = excelService.processExcel(dest);return ResponseEntity.ok().body(result.toMap());} catch (Exception e) {log.error("File upload failed", e);return ResponseEntity.internalServerError().body(Collections.singletonMap("error", e.getMessage()));}}
}

3.2 数据模型与校验规则

@Data
public class EmployeeDTO {@ExcelProperty("员工姓名")@NotBlank(message = "姓名不能为空")private String name;@ExcelProperty("员工工号")@Pattern(regexp = "\\d{8}", message = "工号格式不正确")private String employeeId;@ExcelProperty("所属部门")private String department;@ExcelProperty("入职日期")@DateTimeFormat(pattern = "yyyy-MM-dd")private Date hireDate;
}

3.3 Excel解析与校验

public class EmployeeDataListener extends AnalysisEventListener<EmployeeDTO> {private static final int BATCH_SIZE = 100;private final List<EmployeeDTO> validData = new ArrayList<>();private final List<Map<String, String>> errorList = new ArrayList<>();@Overridepublic void invoke(EmployeeDTO data, AnalysisContext context) {// 基础校验Set<ConstraintViolation<EmployeeDTO>> violations = Validation.buildDefaultValidatorFactory().getValidator().validate(data);// 业务校验if (!departmentService.existByName(data.getDepartment())) {violations.add(new ConstraintViolationImpl<>("部门不存在", null, null, null, null, null));}if (!violations.isEmpty()) {handleErrors(context.readRowHolder().getRowIndex(), violations);return;}validData.add(data);if (validData.size() >= BATCH_SIZE) {saveBatch();validData.clear();}}private void handleErrors(Integer rowNum, Set<ConstraintViolation<?>> violations) {Map<String, String> error = new HashMap<>();error.put("row", String.valueOf(rowNum + 1));error.put("errors", violations.stream().map(ConstraintViolation::getMessage).collect(Collectors.joining(";")));errorList.add(error);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {if (!validData.isEmpty()) {saveBatch();}}private void saveBatch() {employeeService.saveBatch(validData.stream().map(this::convertToEntity).collect(Collectors.toList()));}
}

3.4 服务层实现

@Service
@RequiredArgsConstructor
public class ExcelImportService {private final EmployeeService employeeService;private final DepartmentService departmentService;public ImportResult processExcel(File excelFile) {try (ExcelReader excelReader = EasyExcel.read(excelFile, EmployeeDTO.class, new EmployeeDataListener(employeeService, departmentService)).build()) {ReadSheet readSheet = EasyExcel.readSheet(0).headRowNumber(1).build();excelReader.read(readSheet);return ImportResult.success().errorRecords(listener.getErrorList()).totalCount(listener.getTotalCount()).build();}}
}

四、关键问题处理

4.1 大文件处理优化

  • 使用SXSSF模式(POI的流式API)
  • 分批次提交数据库事务
  • 异步处理(@Async + 线程池)
@Async("excelTaskExecutor")
public Future<ImportResult> asyncProcess(File file) {// 处理逻辑
}

4.2 数据验证策略

验证类型实现方式示例
格式验证JSR-303注解校验@Pattern(regexp=…)
业务逻辑验证数据库查询校验部门是否存在
唯一性验证数据库唯一索引+缓存去重工号唯一性
关联数据验证预加载缓存数据批量校验预加载部门列表

4.3 异常处理机制

@ControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(MultipartException.class)public ResponseEntity<?> handleSizeExceeded() {return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE).body(Collections.singletonMap("error", "文件大小超过限制"));}@ExceptionHandler(ExcelAnalysisException.class)public ResponseEntity<?> handleExcelError() {return ResponseEntity.badRequest().body(Collections.singletonMap("error", "Excel解析失败"));}
}

五、测试验证

  1. 准备测试文件(包含正确和错误数据)
  2. 使用Postman发送POST请求
curl -X POST -F "file=@test.xlsx" http://localhost:8080/api/excel/upload
  1. 查看响应结果:
{"success": true,"total": 150,"successCount": 132,"errorCount": 18,"errors": [{"row": 5, "errors": "部门不存在"},{"row": 17, "errors": "工号格式不正确"}]
}
  1. 检查数据库记录
SELECT * FROM employee WHERE hire_date > '2023-01-01';

六、生产级优化建议

  1. 安全增强

    • 文件病毒扫描
    • 文件头校验防止伪装扩展名
    • 上传频率限制
  2. 性能优化

    • 使用Redis缓存部门数据
    • 多线程分片处理
    • 数据库批量插入优化
  3. 可观测性

    • 添加处理进度查询接口
    • 集成Prometheus监控指标
    • 详细操作日志记录
  4. 用户体验

    • 生成错误报告Excel
    • 支持断点续传
    • 邮件通知处理结果

完整代码示例已托管至GitHub:springboot-excel-demo

通过本文的实现,我们构建了一个健壮的Excel处理流程,能够应对企业级应用中的复杂数据处理需求。实际项目中可根据具体业务场景扩展验证规则和优化处理逻辑。

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

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

相关文章

使用Java爬虫按关键字搜索淘宝商品?

在电商领域&#xff0c;通过关键字搜索商品是获取商品信息的常见需求。Java爬虫技术可以帮助我们自动化地获取这些信息&#xff0c;提高工作效率。本文将详细介绍如何使用Java爬虫按关键字搜索淘宝商品&#xff0c;并提供完整的代码示例。 一、准备工作 1. 注册淘宝开放平台账…

【Git】5 个分区的切换方式及示例

目录 1. **工作区&#xff08;Working Directory&#xff09;**2. **缓存区&#xff08;Stage/Index&#xff09;**3. **本地仓库&#xff08;Local Repository&#xff09;**4. **远程仓库&#xff08;Remote Repository&#xff09;**5. **贮藏区&#xff08;Stash&#xff0…

【计算机视觉】YOLO语义分割

一、语义分割简介 1. 定义 语义分割&#xff08;Semantic Segmentation&#xff09;是计算机视觉中的一项任务&#xff0c;其目标是对图像中的每一个像素赋予一个类别标签。与目标检测只给出目标的边界框不同&#xff0c;语义分割能够在像素级别上区分不同类别&#xff0c;从…

MATLAB之数据分析图系列:从二维到三维(直接套用)

MATLAB以其强大的矩阵运算和可视化功能&#xff0c;成为科研、工程领域的标配工具。本文提供从基础二维图形到复杂三维模型的即用代码块&#xff0c;涵盖数据标注、多图排版、动态演示等核心技巧 所有代码均经过MATLAB 2023a实测&#xff0c;替换数据即可生成专业级图表。” …

HTTP响应数据包全面解析:结构、原理与最佳实践

目录 HTTP响应概述 HTTP响应数据包结构 2.1 状态行 2.2 响应头 2.3 空行 2.4 响应体 HTTP状态码详解 3.1 1xx信息响应 3.2 2xx成功响应 3.3 3xx重定向 3.4 4xx客户端错误 3.5 5xx服务器错误 常见HTTP响应头字段 响应体内容类型 缓存控制机制 实际HTTP响应示例分…

H.264编码解析与C++实现详解

一、H.264编码核心概念 1.1 分层编码结构 H.264采用分层设计&#xff0c;包含视频编码层&#xff08;VCL&#xff09;和网络抽象层&#xff08;NAL&#xff09;。VCL处理核心编码任务&#xff0c;NAL负责封装网络传输数据。 1.2 NALU单元结构 // NAL单元头部结构示例 struc…

快速入手-基于Django-rest-framework的自身组件权限认证(九)

1、在对应的视图函数里增加认证&#xff08;局部起作用&#xff0c;不全局生效&#xff09; 导入类&#xff1a; from rest_framework.authentication import ( BasicAuthentication, SessionAuthentication, ) from rest_framework.permissions import IsAuthentica…

受控组件和非受控组件的区别

在 React 中&#xff0c;​受控组件&#xff08;Controlled Components&#xff09;​ 和 ​非受控组件&#xff08;Uncontrolled Components&#xff09;​ 是处理表单元素的两种不同方式&#xff0c;它们的核心区别在于 ​数据管理的方式 和 ​与 React 的交互模式。 受控组件…

迈向云原生:理想汽车 OLAP 引擎变革之路

在如今数据驱动的时代&#xff0c;高效的分析引擎对企业至关重要。理想汽车作为智能电动汽车的领军企业&#xff0c;面临着海量数据分析的挑战。本文将展开介绍理想汽车 OLAP 引擎从存算一体向云原生架构演进的变革历程&#xff0c;以及在此过程中面临的挑战&#xff0c;以及是…

ZLMediaKit 源码分析——[3] ZLToolKit 中EventPoller之网络事件处理

系列文章目录 第一篇 基于SRS 的 WebRTC 环境搭建 第二篇 基于SRS 实现RTSP接入与WebRTC播放 第三篇 centos下基于ZLMediaKit 的WebRTC 环境搭建 第四篇 WebRTC学习一&#xff1a;获取音频和视频设备 第五篇 WebRTC学习二&#xff1a;WebRTC音视频数据采集 第六篇 WebRTC学习三…

【分布式】分布式限流方案解析

文章目录 固定窗口限流方案​实现方式​优点​缺点​ 滑动窗口限流方案​实现方式​优点​缺点​ 令牌桶限流方案​实现方式​优点​缺点​ 漏斗限流方案​实现方式​优点​缺点​ 在分布式系统蓬勃发展的当下&#xff0c;系统面临的流量挑战日益复杂。为确保系统在高并发场景下…

WPS JS宏编程教程(从基础到进阶)-- 第三部分:JS宏编程语言开发基础

第三部分:JS宏编程语言开发基础 @[TOC](第三部分:JS宏编程语言开发基础)**第三部分:JS宏编程语言开发基础**1. 变量与数据类型**变量声明:三种方式****示例代码****数据类型判断****实战:动态处理单元格类型**2. 运算符全解析**算术运算符****易错点:字符串拼接 vs 数值相…

Python - 爬虫-网页抓取数据-库urllib

urllib库是Python内置的HTTP请求库。无需额外安装&#xff0c;可以直接使用。urllib库包含以下四个模块。 urllib.request - 打开和读取 URL。urllib.error - 包含 urllib.request 抛出的异常。urllib.parse - 解析 URL。urllib.robotparser - 解析 robots.txt 文件。 1、reque…

C++进阶知识复习 1~15

C 进阶总复习 &#xff08;1~15&#xff09; 目的1. 介绍下程序从编写到可执行的整个过程2. C中的auto和decltype的区别3. 介绍下多态的实现原理4. C中的new[] 和delete[] 为什么一定要配对使用&#xff1f;5. C中malloc申请的内存 可以使用delete释放嘛6. 什么情况下会出现内存…

输电线路航空标志球:低空飞行的安全路标 / 恒峰智慧科技

在现代社会&#xff0c;随着航空业的快速发展&#xff0c;低空飞行活动日益频繁。为了确保飞行安全&#xff0c;避免飞机与高压电线等障碍物发生碰撞&#xff0c;输电线路航空标志球应运而生。这种装置被广泛应用于高压输电线路上&#xff0c;尤其是超高压和跨江输电线&#xf…

Debian/Ubuntu的networking的`/etc/network/interfaces`配置文件详解

Debian/Ubuntu的networking的/etc/network/interfaces配置文件详解 Debian/Ubuntu 的 /etc/network/interfaces 配置文件详解 在 Debian/Ubuntu 系统中&#xff0c;/etc/network/interfaces 是传统网络接口配置文件&#xff0c;用于定义网络接口的静态/动态配置。以下是逐项解…

OpenCV 图形API(或称G-API)(1)

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 引言 OpenCV 图形API&#xff08;或称G-API&#xff09;是一个新的OpenCV模块&#xff0c;旨在使常规图像处理更快且更便携。通过引入一种新的基于图的执行…

Leetcode 3505. Minimum Operations to Make Elements Within K Subarrays Equal

Leetcode 3505. Minimum Operations to Make Elements Within K Subarrays Equal 1. 解题思路2. 代码实现 题目链接&#xff1a;3505. Minimum Operations to Make Elements Within K Subarrays Equal 1. 解题思路 这一题大的思路上不难想到就是一个动态规划的思路。我们分别…

win10之mysql server 8.0.41安装

一 mysql server 下载 官网下载地址页面 https://dev.mysql.com/downloads/mysql/二 免装版使用步骤 1 解压 下载完成后,解压文件夹,如下所示: 2 执行安装命令 D:\soft\mysql\mysql-8.0.41-winx64\mysql-8.0.41-winx64\bin>mysqld --install Service successfully in…

第十二届蓝桥杯省赛软件类(cc++组)

第一题&#xff08;空间&#xff09; 解题思路 答案 #include <stdio.h>int main() {// 计算256MB对应的字节数&#xff0c;1MB 1024KB&#xff0c;1KB 1024Blong long total_bytes 256 * 1024 * 1024; // 每个32位二进制整数占4个字节&#xff08;32 / 8 4&#xf…