在这里插入图片描述

/*** 文件分片上传管理器* 提供文件分片、哈希计算、并发上传和断点续传功能*/
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() 基础运算功能&…