Springboot实现使用断点续传优化同步导入Excel

springboot实现使用断点续传优化同步导入Excel

  • 需求前言
  • 断点续传
  • 前端实现
  • 后端实现
  • 完结撒花,如有需要收藏的看官,顺便也用发财的小手点点赞哈,如有错漏,也欢迎各位在评论区评论!

需求前言

在跨境电商系统中,其中下单方式之一就是通过Excel记录多个用户该下单哪些商品实现批量下单,就需要实现导入Excel的方案,最简单的就是同步导入Excel,但同步导入大Excel文件时,网络原因抑或误刷新了页面,就需要重新上传,这就造成用户体验感不好,于是引入断点续传来优化同步导入Excel。

断点续传

断点续传就是在文件上传或下载过程中,如果中途中断了,下次可以从中断的地方继续,而不需要重新开始。这对于大文件传输特别有用,节省时间和带宽。
前端部分需要支持分片上传。也就是说,把大文件切成多个小块,每个小块单独上传。这样即使中间断了,只需要传剩下的部分。前端怎么做呢?本文使用JavaScript的File API来读取文件,然后分片。比如用File.slice方法,把文件切成多个Blob。然后每个Blob单独上传,并告诉服务器这是第几个分片,总共有多少分片,文件的唯一标识是什么,比如用MD5哈希之类的。这样服务器就知道怎么合并这些分片了。

然后,后端操作需要接收这些分片,保存起来,并且记录哪些分片已经上传了。当所有分片都上传完后,后端要把这些分片按顺序合并成完整的文件。这里可能涉及到文件存储的问题,每个分片保存为临时文件,最后合并的时候可能需要按顺序读取所有分片然后写入到一个文件中。另外,为了支持断点续传,前端在上传前可能需要先询问服务器,这个文件已经传了哪些分片,然后只传剩下的。所以后端还需要提供一个接口,让前端可以查询某个文件的上传进度。
具体步骤:
1、前端选择文件后,先计算文件的唯一标识(比如MD5),并查询服务器该文件的上传情况,获取已上传的分片列表。
2、根据分片大小(比如1MB),将文件分片。
3、遍历所有分片,对于未上传的分片,逐个上传到服务器。
4、每个分片上传时,携带分片索引、总片数、文件唯一标识等信息。
5、后端接收到分片后,保存分片文件,并记录该分片已上传。
6、当所有分片上传完毕后,前端发送一个合并请求,或者后端检测到所有分片都已上传后自动合并分片为完整文件。
7、合并完成后,删除临时分片文件。

前端实现

<input type="file" id="fileInput" accept=".xlsx,.xls" />
<button onclick="startUpload()">上传Excel</button>
<div id="progress"></div><script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.0/spark-md5.min.js"></script>
<script>
const CHUNK_SIZE = 2 * 1024 * 1024; // 2MB分片async function startUpload() {const file = document.getElementById('fileInput').files[0];if (!file || !file.name.match(/\.(xlsx|xls)$/i)) {alert('请选择Excel文件');return;}// 1. 生成文件唯一标识const fileId = await computeFileHash(file);document.getElementById('progress').innerHTML = '正在验证文件...';// 2. 检查文件状态const { exists, uploadedChunks } = await fetch(`/api/upload/check?fileId=${fileId}`).then(res => res.json());if (exists) {document.getElementById('progress').innerHTML = '文件已存在,直接解析';triggerParse(fileId); // 触发后端解析return;}// 3. 分片上传const totalChunks = Math.ceil(file.size / CHUNK_SIZE);let uploaded = uploadedChunks.length;// 上传未完成的分片(带进度显示)for (let i = 0; i < totalChunks; i++) {if (!uploadedChunks.includes(i)) {await uploadChunk(file, i, fileId);uploaded++;document.getElementById('progress').innerHTML = `上传进度:${Math.round((uploaded / totalChunks) * 100)}%`;}}// 4. 合并并解析const { success } = await fetch(`/api/upload/merge?fileId=${fileId}`).then(res => res.json());if (success) {triggerParse(fileId);} else {alert('文件合并失败');}
}// 触发后端解析
async function triggerParse(fileId) {const result = await fetch(`/api/excel/parse?fileId=${fileId}`).then(res => res.json());document.getElementById('progress').innerHTML = `解析完成,成功导入${result.successCount}条数据`;
}// 其他函数保持原有逻辑...
</script>

上述代码,前端在上传时还可会展示进度条,分片是在前端决定的,每次上传前都需要查询后端关于该文件的分片上传情况,由前端判断该是否上传某分片,在前端分片后,还可以使用并发发送多个分片

后端实现

@RestController
@RequestMapping("/api")
public class ExcelUploadController {@Value("${file.upload.temp-dir}")private String tempDir;@Value("${file.upload.target-dir}")private String targetDir;// 检查文件状态@GetMapping("/upload/check")public Map<String, Object> checkFile(@RequestParam String fileId) {File targetFile = new File(targetDir, fileId + ".xlsx");Map<String, Object> result = new HashMap<>();result.put("exists", targetFile.exists());File chunkDir = new File(tempDir, fileId);if (chunkDir.exists()) {List<Integer> chunks = Arrays.stream(chunkDir.listFiles()).map(f -> Integer.parseInt(f.getName().split("_")[1])).collect(Collectors.toList());result.put("uploadedChunks", chunks);} else {result.put("uploadedChunks", Collections.emptyList());}return result;}// 分片上传@PostMapping("/upload")public ResponseEntity<?> uploadChunk(@RequestParam("file") MultipartFile file,@RequestParam String fileId,@RequestParam int chunkIndex) throws IOException {File chunkDir = new File(tempDir, fileId);if (!chunkDir.exists()) chunkDir.mkdirs();File chunkFile = new File(chunkDir, "chunk_" + chunkIndex);file.transferTo(chunkFile);return ResponseEntity.ok().build();}// 合并文件@GetMapping("/upload/merge")public Map<String, Object> mergeFile(@RequestParam String fileId) throws IOException {File chunkDir = new File(tempDir, fileId);File targetFile = new File(targetDir, fileId + ".xlsx");try (FileOutputStream fos = new FileOutputStream(targetFile)) {// 按顺序合并分片IntStream.range(0, chunkDir.listFiles().length).sorted().forEach(i -> {File chunk = new File(chunkDir, "chunk_" + i);try (FileInputStream fis = new FileInputStream(chunk)) {byte[] buffer = new byte[1024];int len;while ((len = fis.read(buffer)) != -1) {fos.write(buffer, 0, len);}} catch (IOException e) {throw new RuntimeException("合并失败", e);}});}// 清理临时目录FileUtils.deleteDirectory(chunkDir);return Map.of("success", true);}// Excel解析接口@GetMapping("/excel/parse")public Map<String, Object> parseExcel(@RequestParam String fileId) {File excelFile = new File(targetDir, fileId + ".xlsx");// 使用EasyExcel解析ExcelDataListener listener = new ExcelDataListener();try {EasyExcel.read(excelFile, ExcelData.class, listener).sheet().doRead();} catch (Exception e) {return Map.of("success", false,"error", "解析失败: " + e.getMessage(),"successCount", listener.getSuccessCount());}return Map.of("success", true,"successCount", listener.getSuccessCount(),"errors", listener.getErrors());}
}// Excel数据模型
@Data
public class ExcelData {@ExcelProperty("姓名")private String name;@ExcelProperty("年龄")private Integer age;@ExcelProperty("邮箱")private String email;
}// 数据监听器
public class ExcelDataListener extends AnalysisEventListener<ExcelData> {private final List<ExcelData> cachedData = new ArrayList<>();private final List<String> errors = new ArrayList<>();private int successCount = 0;@Overridepublic void invoke(ExcelData data, AnalysisContext context) {// 数据校验if (data.getName() == null || data.getName().isEmpty()) {errors.add("第" + context.readRowHolder().getRowIndex() + "行: 姓名不能为空");return;}cachedData.add(data);if (cachedData.size() >= 100) {saveToDB();cachedData.clear();}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {if (!cachedData.isEmpty()) {saveToDB();}}private void saveToDB() {// 这里实现实际入库逻辑successCount += cachedData.size();}// Getter省略...
}

使用easyExcel来接收分片,即使前端是并发上传多个分片(当然也最好有个上传限制,比如限制前端控制上传5个分片),也能避免出现OOM和CPU飙升的情况。

Java解析、生成Excel比较有名的框架有apache poi、jxl。但他们都存在一个严重的问题就是非常耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但poi还是有一些缺陷,比如07版本Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。
而easyExcel重写了poi对07版本Excel的解析(一个3M的Excel用POI
SAX解析依然需要100M左右内存)改用easyExcel可以降低到几M,并且再打的Excel也不会出现内存溢出;03版本依赖POI的sax模式,在上层做了模型转换的封装,让使用者可以简单方便;

完结撒花,如有需要收藏的看官,顺便也用发财的小手点点赞哈,如有错漏,也欢迎各位在评论区评论!

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

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

相关文章

【江协科技STM32】软件SPI读写W25Q64芯片(学习笔记)

SPI通信协议及S为5Q64简介&#xff1a;【STM32】SPI通信协议&W25Q64Flash存储器芯片&#xff08;学习笔记&#xff09;-CSDN博客 STM32与W25Q64模块接线&#xff1a; SPI初始化&#xff1a; 片选SS、始终SCK、MOSI都是主机输出引脚&#xff0c;输出引脚配置为推挽输出&…

C 语 言 --- 扫 雷 游 戏(初 阶 版)

C 语 言 --- 扫 雷 游 戏 初 阶 版 代 码 全 貌 与 功 能 介 绍扫雷游戏的功能说明游 戏 效 果 展 示游 戏 代 码 详 解game.htest.cgame.c 总结 &#x1f4bb;作 者 简 介&#xff1a;曾 与 你 一 样 迷 茫&#xff0c;现 以 经 验 助 你 入 门 C 语 言 &#x1f4a1;个 人 主…

数据库基础知识

目录 一、什么是数据库&#xff1f; 二、基本使用方法 &#xff08;1&#xff09;启动服务器进程 &#xff08;2&#xff09;连接服务器 &#xff08;3&#xff09;基本sql语句 三、MySQL架构 四、SQL语句分类 五、存储引擎是什么 一、什么是数据库&#xff1f; 数据库…

在线生成自定义二维码

在线生成自定义二维码 1. 引言 二维码已成为现代互联网的重要工具&#xff0c;广泛应用于链接分享、支付、身份认证等场景。然而&#xff0c;很多在线二维码生成工具功能有限&#xff0c;难以满足个性化需求。如果你需要 自定义颜色、Logo、不同形状的二维码&#xff0c;那么…

DeepSeek处理多模态数据的技术要点和实现方式

DeepSeek具备处理多模态数据的能力&#xff0c;以下是相关技术要点和实现方式。 1. ‌多模态模型架构‌ ‌单流/双流网络‌&#xff1a;通过将文本和图像输入统一编码器&#xff08;单流&#xff09;或分别编码后交互&#xff08;双流&#xff09;实现模态融合‌。‌预训练模…

系统架构设计知识体系总结

1.技术选型 1.什么是技术选型&#xff1f; 技术选型是指评估和选择在项目或系统开发中使用的最合适的技术和工具的过程。这涉及考虑基于其能力、特性、与项目需求的兼容性、可扩展性、性能、维护和其他因素的各种可用选项。技术选型的目标是确定与项目目标相符合、能够有效解…

数智读书笔记系列022《算力网络-云网融合2.0时代的网络架构与关键技术》读书笔记

一、书籍核心价值与定位 1.1 书籍概述:中国联通研究院的权威之作 《算力网络 —— 云网融合 2.0 时代的网络架构与关键技术》由中国联通研究院算力网络攻关团队精心撰写,是业界首部系统性探讨云网融合 2.0 与算力网络的专著。在云网融合从 1.0 迈向 2.0 的关键节点,本书的…

知识图谱中NLP新技术

知识图谱与自然语言处理&#xff08;NLP&#xff09;的结合是当前人工智能领域的前沿方向&#xff0c;其技术发展呈现多维度融合与场景深化的特点。以下从核心技术突破、应用场景创新及未来趋势三个层面&#xff0c;系统梳理知识图谱中NLP的最新进展&#xff1a; 一、核心技术突…

ASP.NET Web的 Razor Pages应用,配置热重载,解决.NET Core MVC 页面在更改后不刷新

Razor Pages应用&#xff0c;修改页面查看修改效果&#xff0c;如果没有热重载&#xff0c;改一句话跑一次&#xff0c;这个活就没法干了。 1、VS2022中的NuGet中安装RuntimeCompilation Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation 需要配套你的.net sdk版本&#x…

DeepSeek(8):结合Kimi-PPT助手一键生成演示报告

1 生成内容 在Deepseek中生成内容&#xff1a; 帮我创建年度计划&#xff0c;描述《智能枕头》产品的如何在全国销售&#xff0c;计划切分到每个月。从而让我们的老板和团队对报告充满信息。输出的内容我需要放到ppt中进行展示。 使用Deepseek R1模型&#xff0c;如下&#x…

到底爱不爱我

L2-3 到底爱不爱我 古代少女有了心上人时&#xff0c;会悄悄折一条树枝&#xff0c;揪那枝上的叶子&#xff0c;揪一片叶子念一句“爱我”&#xff0c;再揪一片念一句“不爱我”…… 这样揪落最后一片叶子的时候&#xff0c;看看是停在“爱”还是“不爱”。 但聪明的慧娘一眼洞…

网络华为HCIA+HCIP 网络编程自动化

telnetlib介绍 telnetlib是Python标准库中的模块。它提供了实现Telnet功能的类telnetlib.Telnet。这里通过调用telnetlib.Telnet类里的不同方法实现不同功能。 配置云

【10】高效存储MongoDB的用法

目录 一、什么是MongoDB 二、准备工作 &#xff08;1&#xff09;安装MongoDB ​&#xff08;2&#xff09;安装pymongo库 三、连接MongoDB 四、指定数据库 五、指定集合 六、插入数据 &#xff08;1&#xff09; insert 方法 &#xff08;2&#xff09;insert_one(…

datawhale组队学习--大语言模型—task4:Transformer架构及详细配置

第五章 模型架构 在前述章节中已经对预训练数据的准备流程&#xff08;第 4 章&#xff09;进行了介绍。本章主 要讨论大语言模型的模型架构选择&#xff0c;主要围绕 Transformer 模型&#xff08;第 5.1 节&#xff09;、详细 配置&#xff08;第 5.2 节&#xff09;、主流架…

Tomcat虚拟主机配置详解:Centos环境下多域名部署(详细教程!)

&#x1f3e1;作者主页&#xff1a;点击&#xff01; Tomcat服务器&#x1f4dd;专栏&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; ⏰️创作时间&#xff1a;2025年3月18日14点14分 最近在折腾 Tomcat 的时候&…

Java+Html实现前后端客服聊天

文章目录 核心组件网络通信层事件调度层服务编排层 Spring实现客服聊天技术方案对比WebScoket建立连接用户上线实现指定用户私聊群聊离线 SpringBootWebSocketHtmljQuery实现客服聊天1. 目录结构2. 配置类3. 实体类、service、controller4. ChatWebSocketHandler消息处理5.前端…

51c自动驾驶~合集24

我自己的原文哦~ https://blog.51cto.com/whaosoft/11926510 #DriveArena 上海AI Lab又放大招&#xff1a;首个高保真闭环生成仿真平台 仓库链接&#xff1a;https://github.com/PJLab-ADG/DriveArena 项目链接&#xff1a;https://pjlab-adg.github.io/DriveArena/ D…

锦华新材业绩波动明显:偿债能力偏弱,大额分红引关注

《港湾商业观察》施子夫 近期&#xff0c;浙江锦华新材料股份有限公司&#xff08;以下简称&#xff0c;锦华新材&#xff09;收到北交所下发的第二轮审核问询函&#xff0c;公司的上市进程继续推进中。 从两轮审核问询函中监管层关注的问题来看&#xff0c;有关锦华新材业绩…

【Node.js入门笔记9---path 模块】

Node.js入门笔记9 Node.js---path 模块一、核心功能0.学习path的前提1. 使用 path.join() 安全拼接路径2. path.resolve()&#xff0c;路径解析&#xff08;绝对路径&#xff09;3. 路径信息提取4. 路径规范化 二、跨平台关键点1. 路径分隔符2. 环境变量分隔符3. 路径格式解析4…

C++20 中 `constexpr` 的强大扩展:算法、工具与复数库的变革

文章目录 一、constexpr 在 <algorithm> 中的应用1. 编译时排序2. 编译时查找 二、constexpr 在 <utility> 中的应用1. 编译时交换2. 编译时条件交换 三、constexpr 在 <complex> 中的应用1. 编译时复数运算 四、总结 C20 对 constexpr 的增强是其最引人注目…