JS实现大文件切片上传以及断点续传
切片上传的原理是:
1.因为file对象的基类是blob,所以可以使用slice分割
2.将从input中获取的file对象使用slice进行分割,每5M一片
3.分别上传各个切片,等待切片上传完通知服务端合并(或者传每一片时把切片总数量也传递过去,让服务端自行合并)
断点续传原理:
切片上传完成之后将上传完的索引放到localStrage上,再次上传时判断是否上传,如果已经上传了则跳过这一片
实现代码:
这里是主进程👇
import { getFileId } from './utils/FileUtils'
import { getLocalStrage, setLocalStrage, removeLocalStrage } from './utils/localStrage'
const worker = new Worker(new URL('./uploadWorker.js', import.meta.url))// 监听WebWorker的消息
worker.onmessage = (e) => {const { type, data, filename, fileId, uploadInfo } = e.dataif (type === 'progress') {document.getElementById('progress').innerHTML = `当前上传进度:${data}%`// 更新切片上传日志setLocalStrage(fileId, JSON.stringify(uploadInfo))} else if (type === 'complete') {document.getElementById('progress').innerHTML = `上传完毕!`worker.postMessage({type: 'merge',filename: filename,})// 移除切片上传日志removeLocalStrage(fileId)}
}// 监控用户上传数据
const upload = document.getElementById('fileInput')upload?.addEventListener('change', async (e) => {const file = e?.target?.files?.[0]const fileId = await getFileId(file)const infoLocal = getLocalStrage(fileId)const uploadInfo = infoLocal ? JSON.parse(infoLocal) : {fileId: fileId,fileName: file.name,uploadedChunks: [],}worker.postMessage({type: 'upload',file,fileId: fileId,uploadInfo: uploadInfo})
})
这里是工作进程👇
const CHUNK_SIZE = 1024 * 1024 * 5 // 5MBlet uploadInfo = null;// 建通主线程的消息
self.onmessage = (e) => {console.log('收到主线程的消息')const { type, file, filename, fileId, uploadInfo: uploadInfoLocal } = e.dataif (type === 'upload') {// 初始化上传信息uploadInfo = uploadInfoLocal// 开始上传文件uploadFile(file, fileId)} else if (type === 'merge') {mergeFile(filename)}
}// 切片上传文件
function uploadFile(file, fileId) {const fileName = file.name// 获取thunk的数量const chunksCount = Math.ceil(file.size / CHUNK_SIZE)// 获取切片数据const chunks = getChunks(file)uploadQueueChunks(chunks, fileName, chunksCount, fileId)
}/*** 获取切片数据* @param file * @returns */
function getChunks(file) {const chunks = []for(let i = 0; i < file.size; i += CHUNK_SIZE) {chunks.push(file.slice(i, i + CHUNK_SIZE))}return chunks
}/*** 上传队列* @param chunks 切片数据* @param fileName 文件名* @param chunksCount 总切片数* @param maxConcurrency 最大并发数*/
async function uploadQueueChunks(chunks, fileName, chunksCount, fileId, maxConcurrency = 5) {// 正在上传的队列const queue = []// 正在上传的文件数量let activeUploads = 0// 遍历上传切片for(let i = 0; i < chunks.length; i++) {// 如果上传过了就直接跳过if(uploadInfo.uploadedChunks.indexOf(i) > -1) {console.log(`第${i}片已经上传过了`)continue}const chunk = chunks[i]// 判断当前上传队列是否达到最大并发if(activeUploads >= maxConcurrency) {// 等待队列中上传完成await Promise.race(queue)}// 上传切片const uploadPromise = uploadedChunk(chunk, i, fileName, chunksCount).then(res=> {// 上传完成,从队列中移除queue.splice(queue.indexOf(uploadPromise), 1)// 更新上传信息uploadInfo.uploadedChunks.push(i)// 减少正在上传的数量activeUploads--// 发送上传进度self.postMessage({type: 'progress',data: Math.round((uploadInfo.uploadedChunks.length / chunksCount) * 100),filename: fileName,uploadInfo: uploadInfo,fileId});})queue.push(uploadPromise)activeUploads++}await Promise.all(queue)// 发送完成通知self.postMessage({type: 'complete',filename: fileName,fileId});
}/*** 上传切片数据* @param thunk 切片数据* @param index 切片索引* @param name 文件名* @param total 总切片数*/
function uploadedChunk(thunk, index, name, total) {console.log(`上传第${index}片`)const formData = new FormData()formData.append('file', thunk)formData.append('index', index.toString())formData.append('filename', name)formData.append('totalChunks', total)return fetch('http://localhost:3000/upload', {method: 'POST',body: formData})
}function mergeFile(filename) {console.log(`开始合并文件:${filename}`)return fetch(`http://localhost:3000/merge`, {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({ filename })})
}
这里是工具类
/*** 获取文件唯一ID* @param {*} file * @returns */
export async function getFileId(file) {const buffer = await file.arrayBuffer();const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);const hashArray = Array.from(new Uint8Array(hashBuffer));return hashArray.map(byte => byte.toString(16).padStart(2, '0')).join('');}export function getLocalStrage(fileId) {return localStorage.getItem(fileId);
}export function setLocalStrage(fileId, data) {return localStorage.setItem(fileId, data);
}export function removeLocalStrage(fileId) {return localStorage.removeItem(fileId);
}
文件目录是这样的👇
就能实现下面的效果:
相关文章:

JS实现大文件切片上传以及断点续传
切片上传的原理是: 1.因为file对象的基类是blob,所以可以使用slice分割 2.将从input中获取的file对象使用slice进行分割,每5M一片 3.分别上传各个切片,等待切片上传完通知服务端合并(或者传每一片时把切片总数量也传…...

AI编程01-生成前/后端接口对表-豆包(或Deepseek+WPS的AI
前言: 做过全栈的工程师知道,如果一个APP的项目分别是前端/后端两个团队开发的话,那么原型设计之后,通过接口文档进行开发对接是非常必要的。 传统的方法是,大家一起定义一个接口文档,然后,前端和后端的工程师进行为何,现在AI的时代,是不是通过AI能协助呢,显然可以…...

小众宝藏分子生物学实验中常用的软件:InSequence
欢迎使用InSequence,正版免费使用,操作友好,小白也能轻松上手哦~ 1. 全新中文界面与更大操作空间 全中文简洁直观的操作界面,常用功能固定至工具栏,随心自定义更改工具栏,让科研人员能够更快速地上手&…...
【自学笔记】机器学习基础知识点总览-持续更新
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 机器学习重点知识点总览一、机器学习基础概念二、机器学习理论基础三、机器学习算法1. 监督学习2. 无监督学习3. 强化学习 四、机器学习处理流程五、机器学习常见问…...

HCIA综合项目之多技术的综合应用实验
十五 HCIA综合实验 15.1 IP规划 #内网分配网段192.168.1.0 24#内网包括骨干链路和两个用户网段,素以需要划分三个,借两位就够用了192.168.1.0 26--骨干192.168.1.64 26---R1下网络192.168.1.128 26---R2下网络192.168.1.192 26--备用192.168.1.64 26--…...

[免费]Springboot+Vue医疗(医院)挂号管理系统【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的SpringbootVue医疗(医院)挂号管理系统,分享下哈。 项目视频演示 【免费】SpringBootVue医疗(医院)挂号管理系统 Java毕业设计_哔哩哔哩_bilibili 项目介绍 在如今社会上,关于信息上…...

网络基础 【UDP、TCP】
1.UDP 首先我们学习UDP和TCP协议 要从这三个问题入手 1.报头和有效载荷如何分离、有效载荷如何交付给上一层的协议?2.认识报头3.学习该协议周边的问题 UDP报头 UDP我们先从示意图来讲解,认识报头。 UDP协议首部有16位源端口号,16位目的端…...

Linux centos8部署maven3.9.9
Linux环境为centos8 一、环境配置 下载部署maven之前先需要先部署好Java环境,我这里是Java17.0.6 先去官网下载jdk包,然后进行环境配置 vim /etc/profile 配置如下: export JAVA_HOME/root/jdk-17.0.6 #Java的安装目录 export JRE_HOM…...

谈谈云计算、DeepSeek和哪吒
我不会硬蹭热点,去分析自己不擅长的跨专业内容,本文谈DeepSeek和哪吒,都是以这两个热点为引子,最终仍然在分析的云计算。 这只是个散文随笔,没有严谨的上下游关联关系,想到哪里就写到哪里。 “人心中的成见…...

链表(典型算法思想)—— OJ例题算法解析思路
目录 一、2. 两数相加 - 力扣(LeetCode) 算法代码: 1. 初始化 2. 遍历链表并相加 3. 返回结果 举例说明 二、24. 两两交换链表中的节点 - 力扣(LeetCode) 算法代码: 代码思路 举例说明 初始状…...

【C++指南】解锁C++ STL:从入门到进阶的技术之旅
💓 博客主页:倔强的石头的CSDN主页 📝Gitee主页:倔强的石头的gitee主页 ⏩ 文章专栏:《C指南》 期待您的关注 目录 一、STL 是什么 二、STL 的核心组件 2.1 容器(Containers) 2.2 算法&…...
LeetCode刷题---字符串---859
亲密字符串 859. 亲密字符串 - 力扣(LeetCode) 题目: 给你两个字符串 s 和 goal ,只要我们可以通过交换 s 中的两个字母得到与 goal 相等的结果,就返回 true ;否则返回 false 。 交换字母的定义是&…...

数据处理中多线程功能的设计逻辑,及python的多线程实现
数据处理中多线程功能的设计逻辑主要是通过并发编程模型来提高程序的执行效率和响应速度。多线程允许在同一进程中创建多个线程,每个线程独立执行任务,同时共享进程的资源(如内存空间)。这种机制特别适用于I/O密集型任务ÿ…...
DeepSeek-R1技术革命:用强化学习重塑大语言模型的推理能力
引言:低成本高性能的AI新范式 在2025年1月,中国AI公司DeepSeek发布了两个标志性模型——DeepSeek-R1-Zero与DeepSeek-R1,以仅600万美元的训练成本实现了与OpenAI O1系列(开发成本约5亿美元)相当的推理性能,…...
python中的深度学习框架TensorFlow 和 PyTorch 有什么区别?
TensorFlow 和 PyTorch 是目前最流行的两个深度学习框架,它们在设计理念、使用方式和社区支持等方面存在一些显著的区别。以下是它们的主要区别: 1. 设计理念 TensorFlow: 静态计算图:TensorFlow 使用静态计算图,即在运行模型之前需要先定义整个计算图。这使得 TensorFlo…...

用 Python 实现 DeepSeek R1 本地化部署
DeepSeek R1 以其出色的表现脱颖而出,不少朋友想将其本地化部署,网上基于 ollama 的部署方式有很多,但今天我要带你领略一种全新的方法 —— 使用 Python 实现 DeepSeek R1 本地化部署,让你轻松掌握,打造属于自己的 AI…...
Spreadjs与GcExcel
GcExcel VS SpreadJS 前言 报表系统前端化,释放后端压力,调高前端研发产能,但随着报表系统的数据量的增加,浏览器的限制,前端报表已达到瓶颈,用户使用体验逐渐不友好,为解决这一问题,找到新的解决方案,所以写下此篇对比 两者分别是什么? SpreadJS 是一款基于 HTML5…...
vue中使用lodash的debounce(防抖函数)
1、安装 npm i --save lodash.debounce2、引入 import debounce from lodash.debounce3、使用 <van-search v-model"searchValue" placeholder"输入姓名或工号" inputhandleInput />第一种: handleInput: debounce(function (val) {c…...
什么是耐环境环形光源
耐环境环形光源是一种专为工业视觉系统设计的光源,能够在恶劣环境下稳定工作。以下是其主要特点和应用: 特点 耐用性:外壳坚固,通常采用金属或高强度塑料,能承受冲击、振动和温度变化。 防护等级:具备高IP防…...

3dtiles——Cesium ion for Autodesk Revit Add-In插件
一、说明: Cesium已经支持3dtiles的模型格式转换; 可以从Cesium官方Aesset中上传gltf等格式文件转换为3dtiles; 也可以下载插件(例如revit-cesium插件)转换并自动上传到Cesium官方Aseet中。 Revit转3dtiles插件使用…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...