前端大文件上传实现切片上传 + 断点续传 + 秒传 + 暂停上传 + 恢复上传

前情提要:

在前端无论是Vue还是React技术栈,都离不开上传业务代码

一般情况下,前端上传文件就是new FormData,然后把文件 append 进去,然后post发送给后端就完事了,但是大文件可不能这么搞,因为美元办法预料到各种意外事件,用户浏览器退出,网络情况不好,网络断开等等,会造成我们的上传直接清零,这种在大文件,网络情况慢,传输时间长的条件下十分容易出现那我们应该如何去优化和解决这个问题呢

对于大文件上传了,确保能让用户在上传的时候想停就停(暂停功能),就算断网了也能继续接着上传(断点上传),如果是之前上传过这个文件了(服务器还存着),就不需要做二次上传了(秒传)

相关知识:浏览器同一域名只能同时请求6个。

一、Web Worker(用于分片)

Web Worker 是一种在Web开发中用于创建多线程环境的API,使得JavaScript代码能够在后台线程中执行,从而不会阻塞主线程的运行。它主要用于处理耗时任务,比如复杂的计算、数据处理等,以提高Web应用的性能和响应速度。所以我们需要采用Web Worker来实现分片。

核心代码:Blob.prototype.slice

文件分片和 Spark-MD5 通常结合使用,用于实现大文件的分片上传和校验,保证数据完整性。所以这边需要借助于spark-md5

Spark-MD5 介绍:它是一个快速的 MD5 算法实现,适用于浏览器和 Node.js 环境。结合文件分片,Spark-MD5 主要用于计算文件或分片的 MD5 校验和,确保数据在传输过程中的完整性。

直接操作

const worker = new Worker(new URL('@/worker/hash-worker.js', import.meta.url)
)

二、上传进度

不严谨版本:我们可以采用常规的onUploadPrigress方法可以实现接口请求进度,可以通过(e.loaded / e.total) * 100)去计算。但是大文件会有些不适用,出现100%也有可能上传失败
严谨版本:服务器返回上传成功的分片数

三、切片上传

vue文件代码

  methods: {handleClick() {// 点击触发选取文件this.$refs.fileInput.dispatchEvent(new MouseEvent('click'))},async hanldeUploadFile() {const fileEle = this.$refs.fileInput// 如果没有文件内容if (!fileEle || !fileEle.files || fileEle.files.length === 0) {return false}const files = fileEle.files// 多文件Array.from(files).forEach(async (item, i) => {const file = item// 单个上传文件let inTaskArrItem = {id: new Date() + i, // 因为forEach是同步,所以需要用指定id作为唯一标识state: 0, // 0不做任何处理,1是计算hash中,2是正在上传中,3是上传完成,4是上传失败,5是上传取消fileHash: '',fileName: file.name,fileSize: file.size,allChunkList: [], // 所有请求的数据whileRequests: [], // 正在请求中的请求个数,目前是要永远都保存请求个数为6finishNumber: 0, //请求完成的个数errNumber: 0, // 报错的个数,默认是0个,超多3个就是直接上传中断percentage: 0, // 单个文件上传进度条}this.uploadFileList.push(inTaskArrItem)// 开始处理解析文件inTaskArrItem.state = 1if (file.size === 0) {// 文件大小为0直接取消该文件上传this.uploadFileList.splice(i, 1)// 继续执行for循环}// 计算文件hashconst { fileHash, fileChunkList } = await this.useWorker(file)console.log(fileHash, '文件hash计算完成')// 解析完成开始上传文件let baseName = ''// 查找'.'在fileName中最后出现的位置const lastIndex = file.name.lastIndexOf('.')// 如果'.'不存在,则返回整个文件名if (lastIndex === -1) {baseName = file.name}// 否则,返回从fileName开始到'.'前一个字符的子串作为文件名(不包含'.')baseName = file.name.slice(0, lastIndex)// 这里要注意!可能同一个文件,是复制出来的,出现文件名不同但是内容相同,导致获取到的hash值也是相同的// 所以文件hash要特殊处理inTaskArrItem.fileHash = `${fileHash}${baseName}`inTaskArrItem.state = 2// 初始化需要上传所有切片列表inTaskArrItem.allChunkList = fileChunkList.map((item, index) => {return {// 总文件hashfileHash: `${fileHash}${baseName}`,// 总文件sizefileSize: file.size,// 总文件namefileName: file.name,index: index,// 切片文件本身chunkFile: item.chunkFile,// 单个切片hash,以 - 连接chunkHash: `${fileHash}-${index}`,// 切片文件大小chunkSize: this.chunkSize,// 切片个数chunkNumber: fileChunkList.length,// 切片是否已经完成finish: false,}})//  逐步对单个文件进行切片上传this.uploadSignleFile(inTaskArrItem)})console.log(this.uploadFileList, 'uploadFileList')},// 生成文件 hash(web-worker)useWorker(file) {return new Promise((resolve) => {const worker = new Worker(new URL('@/worker/hash-worker.js', import.meta.url),{type: 'module',})worker.postMessage({ file, chunkSize: this.chunkSize })worker.onmessage = (e) => {const { fileHash, fileChunkList } = e.dataif (fileHash) {resolve({fileHash,fileChunkList,})}}})},},

work文件代码

import SparkMD5 from './spark-md5.min.js'
// 创建文件切片
function createFileChunk(file, chunkSize) {return new Promise((resolve, reject) => {let fileChunkList = []let cur = 0while (cur < file.size) {// Blob 接口的 slice() 方法创建并返回一个新的 Blob 对象,该对象包含调用它的 blob 的子集中的数据。fileChunkList.push({ chunkFile: file.slice(cur, cur + chunkSize) })cur += chunkSize}// 返回全部文件切片resolve(fileChunkList)})
}// 加载并计算文件切片的MD5
async function calculateChunksHash(fileChunkList) {// 初始化脚本const spark = new SparkMD5.ArrayBuffer()// 计算切片进度(拓展功能,可自行添加)let percentage = 0// 计算切片次数let count = 0// 递归函数,用于处理文件切片async function loadNext(index) {if (index >= fileChunkList.length) {// 所有切片都已处理完毕return spark.end() // 返回最终的MD5值}return new Promise((resolve, reject) => {const reader = new FileReader()reader.readAsArrayBuffer(fileChunkList[index].chunkFile)reader.onload = (e) => {count++spark.append(e.target.result)// 更新进度并处理下一个切片percentage += 100 / fileChunkList.lengthself.postMessage({ percentage }) // 发送进度到主线程resolve(loadNext(index + 1)) // 递归调用,处理下一个切片}reader.onerror = (err) => {reject(err) // 如果读取错误,则拒绝Promise}})}try {// 开始计算切片const fileHash = await loadNext(0) // 等待所有切片处理完毕self.postMessage({ percentage: 100, fileHash, fileChunkList }) // 发送最终结果到主线程self.close() // 关闭Worker} catch (err) {self.postMessage({ name: 'error', data: err }) // 发送错误到主线程self.close() // 关闭Worker}
}// 监听消息
self.addEventListener('message',async (e) => {try {const { file, chunkSize } = e.dataconst fileChunkList = await createFileChunk(file, chunkSize) // 创建文件切片await calculateChunksHash(fileChunkList) // 等待计算完成} catch (err) {// 这里实际上不会捕获到calculateChunksHash中的错误,因为错误已经在Worker内部处理了// 但如果未来有其他的异步操作,这里可以捕获到它们console.error('worker监听发生错误:', err)}},false
)// 主线程可以监听 Worker 是否发生错误。如果发生错误,Worker 会触发主线程的error事件。
self.addEventListener('error', function (event) {console.log('Worker触发主线程的error事件:', event)self.close() // 关闭Worker
})

秒传

原理:在上传文件之前先询问这个文件服务器端是否已存在,若已存在,则不需要上传了,直接让前端显示“已上传成功!”

断点上传

原理:询问后端这个文件我给你上传了多少切片,然后在前端把剩下的切片上传一下,然后再调用一下文件合并接口。

暂停上传

我们需要用到axios的AbortController方法去取消接口请求,或者使用cancelToekn方法也可以。

继续/恢复上传

原理:继续开始把剩下的分片的传给后台即可。

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

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

相关文章

【YOLOv8系列】(三)YOLOv8应用实践:从识别到分类再到分割的全方位视觉解决方案

随着深度学习技术的不断演进&#xff0c;目标检测领域的明星模型——YOLO系列&#xff0c;已经发展到了第八代&#xff0c;即YOLOv8。这一版本不仅继承了前辈们快速与准确的特点&#xff0c;还实现了功能上的巨大飞跃&#xff0c;成为了一个全能型的视觉AI工具箱&#xff0c;支…

如何实现一个二叉搜索树

实现一个二叉搜索树&#xff08;Binary Search Tree, BST&#xff09;主要涉及定义树的结构、插入新节点、搜索节点、以及可能的其他操作&#xff0c;如删除节点、遍历树等。下面是一个简单的二叉搜索树的实现示例&#xff0c;使用Python语言&#xff1a; 定义树节点 首先&am…

maven的settings.xml无法正确配置本地仓库路径

因为以前使用过新版的maven&#xff0c;现在要换个版本使用。 在配置新的本地仓库路径的时候突然发现居然idea居然识别不了我settings.xml里面配置的路径。 我很是震惊&#xff0c;明明之前一直都是这样子配置的。怎么突然间不行了。当我冥思苦想&#xff0c;在网上搜寻资料无果…

WPF学习(5) -- WPF绑定

一、双向绑定 1.代码示例 <Window x:Class"学习.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expres…

在 MATLAB 中,如何高效地处理大规模矩阵运算以提高程序的运行速度?

在 MATLAB 中&#xff0c;可以采用以下一些方法来高效地处理大规模矩阵运算以提高程序的运行速度&#xff1a; 避免使用循环&#xff1a;MATLAB 是一种矢量化编程语言&#xff0c;通过使用矢量和矩阵操作&#xff0c;可以避免使用循环来处理矩阵运算。避免循环可以大大提高程序…

[论文阅读]MaIL: Improving Imitation Learning with Mamba

Abstract 这项工作介绍了mamba模仿学习&#xff08;mail&#xff09;&#xff0c;这是一种新颖的模仿学习&#xff08;il&#xff09;架构&#xff0c;为最先进的&#xff08;sota&#xff09;变换器策略提供了一种计算高效的替代方案。基于变压器的策略由于能够处理具有固有非…

阿里云产品流转

本文主要记述如何使用阿里云对数据进行流转&#xff0c;这里只是以topic流转&#xff08;再发布&#xff09;为例进行说明&#xff0c;可能还会有其他类型的流转&#xff0c;不同服务器的流转也可能会不一样&#xff0c;但应该大致相同。 1 创建设备 具体细节可看&#xff1a;…

LangChain —— Prompt Templates —— How to use few shot examples in chat models

文章目录 一、概述二、固定示例 Fixed Example 一、概述 本指南介绍了如何使用示例输入和输出提示 chat model。为模型提供几个这样的例子被称为 few-shotting&#xff0c;这是一种简单而强大的方法来指导生成&#xff0c;在某些情况下可以大大提高模型性能。 对于如何最好地进…

Spring源码中的模板方法模式

1. 什么是模板方法模式 模板方法模式&#xff08;Template Method Pattern&#xff09;是一种行为设计模式&#xff0c;它在操作中定义算法的框架&#xff0c;将一些步骤推迟到子类中。模板方法让子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板方法模式的定义&…

驱动LSM6DS3TR-C实现高效运动检测与数据采集(8)----中断获取FIFO数据并应用MotionFX库解析空间坐标

驱动LSM6DS3TR-C实现高效运动检测与数据采集.8--中断获取FIFO数据并应用MotionFX库解析空间坐标 概述视频教学样品申请源码下载开启LED开启INT中断参考驱动程序中断读取传感器数据主程序演示 概述 本文将探讨如何使用中断机制获取FIFO数据并应用MotionFX库解析空间坐标。Motio…

React Native Android 应用开发、调试与发布深度指南

React Native 是构建跨平台移动应用的强大工具。本指南将深入探讨使用 React Native 开发、调试和发布 Android 应用的各个方面&#xff0c;为您提供全面的指导。 1. 环境准备 Node.js 和 npm/yarn: 确保您的系统中安装了 Node.js 和 npm (Node Package Manager) 或 yarn。您…

在 PostgreSQL 里如何处理数据的索引碎片整理的自动化?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 PostgreSQL 中数据索引碎片整理的自动化处理 PostgreSQL 中数据索引碎片整理的自动化处理 在数据库管…

接口基础知识2:http通信的组成

课程大纲 一、http协议 HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网中被使用最广的一种网络协议&#xff0c;用于客户端与服务器之间的通信。 HTTP协议定义了一系列的请求方法&#xff0c;例如 GET、POST、PUT、DELETE 等&…

特惠电影票api安全性如何评测

评测特惠电影票API的安全性是确保用户数据安全和系统稳定运行的关键步骤。以下是评测特惠电影票API安全性的一些方法和步骤&#xff1a; ### 1. **认证和授权** - **JWT认证**&#xff1a;使用JSON Web Token (JWT) 进行用户身份验证和授权&#xff0c;确保只有合法用户可以访…

Ubuntu搭建Android架构so库交叉编译环境

目录 前言一、下载NDK并安装二、安装NDK三、配置交叉编译工具链四、编写交叉编译脚本 前言 需要将一些源码编译成Android可用的架构的so库 一、下载NDK并安装 https://developer.android.google.cn/ndk/downloads/ 二、安装NDK 将下载下来的android-ndk-r23b-linux.zip解压…

uniapp内置组件scroll-view案例解析

参考资料 文档地址&#xff1a;https://uniapp.dcloud.net.cn/component/scroll-view.html 官方给的完整代码 <script>export default {data() {return {scrollTop: 0,old: {scrollTop: 0}}},methods: {upper: function(e) {console.log(e)},lower: function(e) {cons…

Python面试题:在 Python 中,如何实现一个 LRU(最近最少使用)缓存?

在 Python 中&#xff0c;可以使用 collections.OrderedDict 或 functools.lru_cache 来实现一个 LRU&#xff08;最近最少使用&#xff09;缓存。functools.lru_cache 是一个内置装饰器&#xff0c;可以直接用于缓存函数的结果。而如果你想实现一个自定义的 LRU 缓存&#xff…

【SVPWM】空间矢量调制

SVPWM仿真模型 扇区315462 切换时间马鞍波 三角载波和马鞍波比较 三相电压 FFT分析 参考文献&#xff1a; 现代永磁同步电机控制原理及MATLAB仿真.袁雷.

live555关于RTSP协议交互流程

RTP在和h264 RTP在和h265 RTP载荷AAC live555闭环双向链表 概要 rtsp在交互的过程中用到很多协议:tcp,udp,rtp,rtcp,sdp等协议&#xff1b;该篇文章主要分析在live555中这些协议是什么时候被创建的&#xff0c;什么时候被使用的等协议相关流程。 TCP&#xff1a;服务器与客…

WPF学习(3) -- 控件模板

一、操作过程 二、代码 <Window x:Class"学习.MainWindow"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.microsoft.com/expressio…