【大文件上傳】分片上傳+斷點續傳+Worker線程計算Hash

在這里插入圖片描述

/*** 文件分片上傳管理器* 提供文件分片、哈希計算、并發上傳和斷點續傳功能*/
class FileChunkUploader {/*** 構造函數* @param {File} file - 要上傳的文件對象* @param {Object} options - 配置選項* @param {number} [options.chunkSize=5MB] - 每個分片的大小(字節)* @param {number} [options.maxConcurrency=3] - 最大并發上傳數*/constructor(file, options = {}) {this.sourceFile = file;this.config = {chunkSize: options.chunkSize || 5 << 20, // 默認5MBmaxConcurrency: options.threads || 3, // 并發數hashWorkerPath: options.hashWorkerPath || 'hash-worker.js'};this.uploadState = {totalChunks: Math.ceil(file.size / this.config.chunkSize),uploadedChunkIndices: new Set(),fileHash: null,uploadSessionId: this._generateUniqueId()};}/*** 啟動上傳流程* @returns {Promise<Object>} 上傳結果*/async startUpload() {try {// 1. 計算文件哈希this.uploadState.fileHash = await this._calculateFileHash();// 2. 檢查是否可秒傳if (await this._checkForInstantUpload()) {return { success: true, skipped: true, reason: '文件已存在' };}// 3. 獲取已上傳分片進度await this._fetchUploadProgress();// 4. 執行分片上傳return await this._uploadAllChunks();} catch (error) {console.error('上傳失敗:', error);throw new UploadError('UPLOAD_FAILED', { cause: error });}}/*** 使用Web Worker計算文件哈希* @private* @returns {Promise<string>} 文件哈希值*/async _calculateFileHash() {return new Promise((resolve) => {const worker = new Worker(this.config.hashWorkerPath);worker.postMessage({ file: this.sourceFile,operation: 'hash'});worker.onmessage = (event) => {if (event.data.progress) {this._updateProgress(event.data.progress);} else if (event.data.hash) {resolve(event.data.hash);worker.terminate();}};});}/*** 檢查服務器是否已存在該文件* @private* @returns {Promise<boolean>}*/async _checkForInstantUpload() {const response = await fetch(`/api/files/check?hash=${this.uploadState.fileHash}`);const { exists } = await response.json();return exists;}/*** 獲取已上傳分片信息* @private*/async _fetchUploadProgress() {try {const response = await fetch(`/api/uploads/progress?sessionId=${this.uploadState.uploadSessionId}`);const { uploadedChunks } = await response.json();uploadedChunks.forEach(index => {this.uploadState.uploadedChunkIndices.add(index);});} catch (error) {console.warn('獲取上傳進度失敗,將重新上傳所有分片', error);}}/*** 上傳所有未完成的分片* @private*/async _uploadAllChunks() {const pendingChunks = this._getPendingChunks();await this._uploadWithConcurrencyControl(pendingChunks);return this._finalizeUpload();}/*** 獲取待上傳的分片索引* @private* @returns {number[]}*/_getPendingChunks() {return Array.from({ length: this.uploadState.totalChunks },(_, index) => index).filter(index => !this.uploadState.uploadedChunkIndices.has(index));}/*** 帶并發控制的分片上傳* @private* @param {number[]} chunkIndices - 待上傳分片索引*/async _uploadWithConcurrencyControl(chunkIndices) {const activeUploads = new Set();for (const chunkIndex of chunkIndices) {const uploadPromise = this._uploadSingleChunk(chunkIndex).finally(() => activeUploads.delete(uploadPromise));activeUploads.add(uploadPromise);if (activeUploads.size >= this.config.maxConcurrency) {await Promise.race(activeUploads);}}await Promise.all(activeUploads);}/*** 上傳單個分片* @private* @param {number} chunkIndex - 分片索引* @param {number} [maxRetries=3] - 最大重試次數*/async _uploadSingleChunk(chunkIndex, maxRetries = 3) {let attempt = 0;while (attempt < maxRetries) {try {const chunkData = this._getChunkData(chunkIndex);await this._sendChunkToServer(chunkIndex, chunkData);this.uploadState.uploadedChunkIndices.add(chunkIndex);this._saveProgressLocally();return;} catch (error) {attempt++;if (attempt >= maxRetries) {throw new UploadError('CHUNK_UPLOAD_FAILED', {chunkIndex,attempts: maxRetries,error});}}}}/*** 獲取分片數據* @private* @param {number} chunkIndex * @returns {Blob}*/_getChunkData(chunkIndex) {const start = chunkIndex * this.config.chunkSize;const end = Math.min(start + this.config.chunkSize, this.sourceFile.size);return this.sourceFile.slice(start, end);}/*** 發送分片到服務器* @private*/async _sendChunkToServer(chunkIndex, chunkData) {const formData = new FormData();formData.append('file', chunkData);formData.append('chunkIndex', chunkIndex);formData.append('totalChunks', this.uploadState.totalChunks);formData.append('fileHash', this.uploadState.fileHash);formData.append('sessionId', this.uploadState.uploadSessionId);const response = await fetch('/api/uploads/chunk', {method: 'POST',body: formData});if (!response.ok) {throw new Error(`服務器返回錯誤: ${response.status}`);}}/*** 完成上傳并合并分片* @private*/async _finalizeUpload() {const response = await fetch('/api/uploads/complete', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({fileHash: this.uploadState.fileHash,sessionId: this.uploadState.uploadSessionId,totalChunks: this.uploadState.totalChunks})});if (!response.ok) {throw new Error('合并分片失敗');}return { success: true };}/*** 生成唯一ID* @private*/_generateUniqueId() {return Math.random().toString(36).substring(2) + Date.now().toString(36);}/*** 本地保存上傳進度* @private*/_saveProgressLocally() {const progressData = {sessionId: this.uploadState.uploadSessionId,uploadedChunks: Array.from(this.uploadState.uploadedChunkIndices),timestamp: Date.now()};localStorage.setItem(`uploadProgress_${this.uploadState.fileHash}`,JSON.stringify(progressData));}
}/*** 上傳錯誤類*/
class UploadError extends Error {constructor(code, details = {}) {super();this.name = 'UploadError';this.code = code;this.details = details;this.message = this._getErrorMessage();}_getErrorMessage() {const messages = {'UPLOAD_FAILED': '文件上傳失敗','CHUNK_UPLOAD_FAILED': `分片上傳失敗 (嘗試次數: ${this.details.attempts})`,'NETWORK_ERROR': '網絡連接出現問題'};return messages[this.code] || '發生未知錯誤';}
}
// hash-worker.js
// 導入 SparkMD5 庫用于計算文件哈希
self.importScripts('spark-md5.min.js');// 監聽主線程消息
self.onmessage = async (event) => {const file = event.data.file;const chunkSize = 2 * 1024 * 1024; // 2MB 的切片大小const totalChunks = Math.ceil(file.size / chunkSize);const hasher = new self.SparkMD5.ArrayBuffer();// 分片計算文件哈希for (let currentChunk = 0; currentChunk < totalChunks; currentChunk++) {const chunkData = await getFileChunk(file, currentChunk * chunkSize, chunkSize);hasher.append(chunkData);// 向主線程發送進度更新self.postMessage({ progress: (currentChunk + 1) / totalChunks });}// 計算完成發送最終哈希值self.postMessage({ hash: hasher.end() });
};/*** 讀取文件指定分片* @param {File} file - 目標文件* @param {number} start - 起始字節位置* @param {number} length - 分片長度* @returns {Promise<ArrayBuffer>}*/
function getFileChunk(file, start, length) {return new Promise((resolve) => {const reader = new FileReader();reader.onload = (loadEvent) => resolve(loadEvent.target.result);reader.readAsArrayBuffer(file.slice(start, start + length));});
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93264.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93264.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93264.shtml

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

相關文章

-bash: ./restart.sh: /bin/bash^M: 壞的解釋器: 沒有那個文件或目錄

這是典型的Windows換行符&#xff08;CRLF&#xff09;導致的腳本不能在Linux/Unix環境下正常執行的問題。Linux 期望的是 LF (\n)&#xff0c;而 Windows 是 CRLF (\r\n)&#xff0c;所以腳本文件的第一行解釋器路徑后多了一個不可見的 ^M&#xff08;回車符&#xff09;&…

芯伯樂1MHz高頻低功耗運放芯片MCP6001/2/4系列,微安級功耗精密信號處理

前言在工業控制、通信設備、家用電器等領域&#xff0c;信號處理是核心環節之一&#xff0c;其中運算放大器&#xff08;運放&#xff09;是實現信號處理的核心器件&#xff0c;其選型參數直接決定了信號鏈路的性能和輸出信號的質量&#xff0c;是確保信號正常、精確輸出的關鍵…

智能的數學原理

智能的數學原理可以分成幾個層次來看——從最底層的數學基礎&#xff0c;到支撐“智能”表現的數學模型&#xff0c;再到連接數學與現實認知的理論框架。 分成 五個核心板塊 來梳理&#xff1a;1. 信息與表示的數學 智能的第一步是“能表示信息”&#xff0c;這涉及&#xff1a…

FPGA即插即用Verilog驅動系列——SPI發送模塊

實現功能&#xff1a;按字節以spi模式3發送數據&#xff0c;如果要stm32接收&#xff0c;請在cubemx中將對應的spi接口設置為模式3&#xff0c;詳情見代碼開頭注釋// spi_byte_master.v // 經過優化的SPI主設備模塊&#xff0c;每次使能發送一個字節。 // 它實現了SPI模式3 (CP…

C++ 排序指南

在 C 中&#xff0c;std::sort 是一個非常強大且常用的函數&#xff0c;用于對容器或數組中的元素進行排序。它定義在 <algorithm> 頭文件中。 std::sort 的基本語法 std::sort 的基本語法有以下幾種形式&#xff1a;默認升序排序&#xff1a; std::sort(first, last);fi…

RS232串行線是什么?

RS232串行線是什么&#xff1f;RS232串行線是一種用于串行通信的標準化接口&#xff0c;廣泛應用于早期計算機、工業設備、儀器儀表等領域的短距離數據傳輸。以下是其核心要點解析&#xff1a;1. 基本定義 全稱&#xff1a;RS232&#xff08;Recommended Standard 232&#xff…

k8s-scheduler 解析

學習文檔 官網的k8s上關于scheduler的文檔基本可以分為這兩部分 介紹 scheduler 的基本概念 介紹 scheduler 的配置 KubeSchedulerConfiguration 的參數 介紹 scheduler 的命令行參數 調度框架解析 Scheduling-framework 解析 kube-scheduler 選擇 node 通過下面這兩步…

前端簡歷1v1修改: 優化項目經驗

今天有人找我優化前端簡歷&#xff0c;分享一下如何優化項目經驗描述。這是修改前的版本&#xff1a;項目為Web前端開發&#xff0c;但描述為APP應用&#xff0c;包含某某功能。起初我感到困惑&#xff0c;因為前端技術棧使用Vue&#xff0c;為何項目類型是APP&#xff1f;后來…

K8S企業級應用與DaemonSet實戰解析

目錄 一、概述 二、YAML文件詳解 三、企業應用案例 3.1 環境準備 3.2 擴縮容 3.3 滾動更新 3.4 回滾 四、自定義更新策略 4.1類型 4.2 設置方式 4.3 配置案例 一、 DaemonSet 概述 DaemonSet 工作原理 Daemonset 典型的應用場景 DaemonSet 與 Deployment 的區別…

Celery在Django中的應用

Celery在Django中的應用一、項目配置二、異步任務2.1 普通用法2.1.1 通過delay2.1.2 通過apply_async2.2 高級用法2.2.1 任務回調&#xff08;Callback&#xff09;2.2.2 任務鏈&#xff08;Chaining&#xff09;2.2.3 任務組&#xff08;Group&#xff09;2.2.4 任務和弦&…

DeepSeek生成的高精度大數計算器

# 高精度計算器&#xff08;精確顯示版&#xff09;1. **精確顯示優化**&#xff1a;- 新增print_mpfr()函數專門處理MPFR數值的打印- 自動移除多余的尾隨零和小數點- 確保所有浮點結果都以完整十進制形式顯示&#xff0c;不使用科學計數法2. **浮點精度修復**&#xff1a;- 所…

08--深入解析C++ list:高效操作與實現原理

1. list介紹1.1. list概述template < class T, class Alloc allocator<T> > class list;Lists are sequence containers that allow constant time insert and erase operations anywhere within the sequence, and iteration in both directions.概述&#xff1…

GraphQL從入門到精通完整指南

目錄 什么是GraphQLGraphQL核心概念GraphQL Schema定義語言查詢(Queries)變更(Mutations)訂閱(Subscriptions)Schema設計最佳實踐服務端實現客戶端使用高級特性性能優化實戰項目 什么是GraphQL GraphQL是由Facebook開發的一種API查詢語言和運行時。它為API提供了完整且易于理…

使用 Dockerfile 與 Docker Compose 結合+Docker-compose.yml 文件詳解

使用 Dockerfile 與 Docker Compose 結合的完整流程 Dockerfile 用于定義單個容器的構建過程&#xff0c;而 Docker Compose 則用于編排多個容器。以下是結合使用兩者的完整方法&#xff1a; 1. 創建 Dockerfile 在項目目錄中創建 Dockerfile 定義應用鏡像的構建過程&#xff1…

15 ABP Framework 開發工具

ABP Framework 開發工具 概述 該頁面詳細介紹了 ABP Framework 提供的開發工具和命令行界面&#xff08;CLI&#xff09;&#xff0c;用于創建、管理和定制 ABP 項目。ABP CLI 是主要開發工具&#xff0c;支持項目腳手架、模塊添加、數據庫遷移管理及常見開發任務自動化。 ABP …

力扣top100(day02-01)--鏈表01

160. 相交鏈表 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode(int x) {* val x;* next null;* }* }*/ public class Solution {/*** 查找兩個鏈表的相交節點* param headA 第一個…

LLM 中 語音編碼與文本embeding的本質區別

直接使用語音編碼,是什么形式,和文本的區別 直接使用語音編碼的形式 語音編碼是將模擬語音信號轉換為數字信號的技術,其核心是對語音的聲學特征進行數字化表征,直接承載語音的物理聲學信息。其形式可分為以下幾類: 1. 基于波形的編碼(保留原始波形特征) 脈沖編碼調制…

模型選擇與調優

一、模型選擇與調優在機器學習中&#xff0c;模型的選擇和調優是一個重要的步驟&#xff0c;它直接影響到最終模型的性能1、交叉驗證在任何有監督機器學習項目的模型構建階段&#xff0c;我們訓練模型的目的是從標記的示例中學習所有權重和偏差的最佳值如果我們使用相同的標記示…

vue+Django農產品推薦與價格預測系統、雙推薦+機器學習預測+知識圖譜

vueflask農產品推薦與價格預測系統、雙推薦機器學習價格預測知識圖譜文章結尾部分有CSDN官方提供的學長 聯系方式名片 文章結尾部分有CSDN官方提供的學長 聯系方式名片 關注B站&#xff0c;有好處&#xff01;編號: D010 技術架構: vueflaskmysqlneo4j 核心技術&#xff1a; 基…

數據分析小白訓練營:基于python編程語言的Numpy庫介紹(第三方庫)(下篇)

銜接上篇文章&#xff1a;數據分析小白訓練營&#xff1a;基于python編程語言的Numpy庫介紹&#xff08;第三方庫&#xff09;&#xff08;上篇&#xff09;&#xff08;十一&#xff09;數組的組合核心功能&#xff1a;一、生成基數組np.arange().reshape() 基礎運算功能&…