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.pswp.cn/pingmian/73079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73079.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73079.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql 對json的處理?

MySQL從5.7版本開始支持JSON數據類型&#xff0c;并提供了多種函數來查詢和處理JSON數據。以下是一些基本的操作和函數&#xff1a; 創建包含JSON列的表&#xff1a; 可以直接在表定義中指定某列為JSON類型。 CREATE TABLE my_table (id INT NOT NULL AUTO_INCREMENT,data JSON…

Nexus L2 L3基本配置

接口基本配置 N7K上所有端口默認處于shutdown狀態; N5K上所有端口默認處于no shutdown狀態(所有端口都是switchport) 默認所有接口都是三層route模式, 只有當線卡不支持三層的時候, 接口才會處于二層switchport模式 show run all | in “system default” 創建SVI口需要提前打…

HCIA-AI人工智能筆記3:數據預處理

統講解數據預處理的核心技術體系&#xff0c;通過Python/Pandas與華為MindSpore雙視角代碼演示&#xff0c;結合特征工程優化實驗&#xff0c;深入解析數據清洗、標準化、增強等關鍵環節。 一、數據預處理技術全景圖 graph TD A[原始數據] --> B{數據清洗} B --> B1[缺…

G-Star 校園開發者計劃·黑科大|開源第一課之 Git 入門

萬事開源先修 Git。Git 是當下主流的分布式版本控制工具&#xff0c;在軟件開發、文檔管理等方面用處極大。它能自動記錄文件改動&#xff0c;簡化合并流程&#xff0c;還特別適合多人協作開發。學會 Git&#xff0c;就相當于掌握了一把通往開源世界的鑰匙&#xff0c;以后參與…

MySQL錯誤 “duplicate entry ‘1‘ for key ‘PRIMARY‘“ 解決方案

文章目錄 1. 錯誤原因分析2. 快速解決方法場景1:手動插入重復值場景2:自增主鍵沖突場景3:批量插入沖突3. 長期預防策略4. 高級排查技巧該錯誤通常由主鍵沖突引起,表示嘗試插入或更新的主鍵值已存在于表中。以下是分步排查和解決方法: 1. 錯誤原因分析 主鍵唯一性約束:表…

WEB攻防-PHP反序列化-字符串逃逸

目錄 前置知識 字符串逃逸-減少 字符串逃逸-增多 前置知識 1.PHP 在反序列化時&#xff0c;語法是以 ; 作為字段的分隔&#xff0c;以 } 作為結尾&#xff0c;在結束符}之后的任何內容不會影響反序列化的后的結果 class people{ public $namelili; public $age20; } var_du…

把生產隊的大模型Grok 3 beta用來實現字帖打磨

第一個版本&#xff0c;就是簡單的田字格&#xff0c;Grok 3 beta 思考了15s就得到了html前端代碼&#xff0c;javascript; 然而還不完美&#xff1b; 第二個版本&#xff0c;進一步&#xff0c;通過pinyin項目給漢字加上注音&#xff0c;米字格和四線格&#xff1b;&#xff…

windows+ragflow+deepseek實戰之一excel表查詢

ragflows平臺部署參考文章 Win10系統Docker+DeepSeek+ragflow搭建本地知識庫 ragflow通過python實現參考這篇文章 ragflow通過python實現 文章目錄 背景效果1、準備數據2、創建知識庫3、上傳數據并解析4、新建聊天助理5、測試會話背景 前面已經基于Win10系統Docker+DeepSeek+…

OpenCV圖像處理基礎2

接著上一篇OpenCV圖像處理基礎1繼續說。 圖像閾值處理 1、簡單閾值處理 ret, thresholded_image = cv2.threshold(image, thresh, maxval, cv2.THRESH_BINARY)thresh 是閾值,maxval 是最大值。 2、自適應閾值處理 thresholded_image = cv2.adaptiveThreshold(image, maxv…

go安裝lazydocker

安裝 先安裝go環境 https://blog.csdn.net/Yqha1/article/details/146430281?fromshareblogdetail&sharetypeblogdetail&sharerId146430281&sharereferPC&sharesourceYqha1&sharefromfrom_link 安裝lazydocker go install github.com/jesseduffield/laz…

【架構】單體架構 vs 微服務架構:如何選擇最適合你的技術方案?

文章目錄 ?前言?一、架構設計的本質差異&#x1f31f;1、代碼與數據結構的對比&#x1f31f;2、技術棧的靈活性 ?二、開發與維護的成本博弈&#x1f31f;1、開發效率的階段性差異&#x1f31f;2、維護成本的隱形陷阱 ?三、部署與擴展的實戰策略&#x1f31f;1、部署模式的本…

C#實現分段三次Hermite插值

目錄 一、Hermite插值介紹 1、功能說明 2、數學方法 二、代碼實現 1、CubicHermiteInterpolator類封裝 2、應用示例 三、導數值的獲取方式 1、數學方法介紹 2、代碼應用示例 四、其它封裝的分段三次Hermite插值類 1、方式一 &#xff08;1&#xff09;封裝代碼 &…

重要重要!!fisher矩陣元素有什么含義和原理; Fisher 信息矩陣的形式; 得到fisher矩陣之后怎么使用

fisher矩陣元素有什么含義和原理 目錄 fisher矩陣元素有什么含義和原理一、對角線元素( F i , i F_{i,i} Fi,i?)的含義與原理二、非對角線元素( F i , j F_{i,j} Fi,j?)的含義與原理Fisher 信息矩陣的形式矩陣的寬度有位置權重數量決定1. **模型參數結構決定矩陣維度**2.…

【STM32】uwTick在程序中的作用及用法,并與Delay函數的區別

一、uwTick 的作用 1.系統時間基準 uwTick 是一個全局變量&#xff08;volatile uint32_t&#xff09;&#xff0c;記錄系統啟動后的毫秒級時間累計值。默認情況下&#xff0c;它由 SysTick 定時器每 ?1ms 自動遞增一次&#xff08;通過 HAL_IncTick() 函數。例如&#xff0…

docker速通

docker 鏡像操作搜索鏡像拉取鏡像查看鏡像刪除鏡像 容器操作!查看容器運行容器run命令詳細介紹 啟動容器停止容器重啟容器查看容器狀態查看容器日志刪除容器進入容器 保存鏡像提交保存加載 分享社區登錄命名推送 docker存儲目錄掛載卷映射查看所有容器卷創建容器卷查看容器卷詳…

OpenCV旋轉估計(5)圖像拼接的一個函數waveCorrect()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 waveCorrect 是OpenCV中用于圖像拼接的一個函數&#xff0c;特別適用于全景圖拼接過程中校正波浪形失真&#xff08;Wave Correction&#xff09…

Python、MATLAB和PPT完成數學建模競賽中的地圖繪制

參加數學建模比賽時&#xff0c;很多題目——諸如統計類、數據挖掘類、環保類、建議類的題目總會涉及到地理相關的情景&#xff0c;往往要求我們制作與地圖相關的可視化內容。如下圖&#xff0c;這是21年亞太賽的那道塞罕壩的題目&#xff0c;期間涉及到溫度、降水和森林覆蓋率…

深入了解 C# 中的 LINQ:功能、語法與應用解析

1. 什么是 LINQ&#xff1f; LINQ&#xff08;Language Integrated Query&#xff0c;語言集成查詢&#xff09;是 C# 和其他 .NET 語言中的一種強大的查詢功能&#xff0c;它允許開發者在語言中直接執行查詢操作。LINQ 使得開發者可以使用 C# 語法&#xff08;或 VB.NET&…

DeepSeek R1 本地部署指南 (3) - 更換本地部署模型 Windows/macOS 通用

0.準備 完成 Windows 或 macOS 安裝&#xff1a; DeepSeek R1 本地部署指南 (1) - Windows 本地部署-CSDN博客 DeepSeek R1 本地部署指南 (2) - macOS 本地部署-CSDN博客 以下內容 Windows 和 macOS 命令執行相同&#xff1a; Windows 管理員啟動&#xff1a;命令提示符 CMD ma…

【總結】Pytest vs Behave,BDD 測試框架哪家強?

引言 在測試驅動開發(TDD)和行為驅動開發(BDD)流行的今天&#xff0c;Pytest和 Behave 成為了 Python 生態中最常見的自動化測試框架。那么&#xff0c;究竟該選擇哪一個&#xff1f;它們各自有哪些優缺點&#xff1f;本篇文章將為你全面解析&#xff01; 1. 什么是 Pytest&a…