大文件分片上传,断点续传,秒传 示例(待更新...)

1.html代码

<template><div class="card content-box"><el-upload ref="upload" class="upload-demo" action="https://run.mocky.io/v3/9d059bf9-4660-45f2-925d-ce80ad6c4d15":limit="1" :on-change="handleFileChange" :auto-upload="false"><template #trigger><el-button type="primary">选择文件</el-button></template><el-button :disabled="uploadDisabled" class="ml-3" type="success" @click="handlerUpload">上传</el-button><el-button class="ml-3" type="success" @click="handlePause">暂停</el-button><el-button class="ml-3" type="success" @click="handleResume">恢复</el-button><el-button class="ml-3" type="success" @click="resetData">重置</el-button><template #tip><br /><br /><span>计算文件hash进度: {{ hashPercentage }}%</span><br /><br /><span>上传进度:{{ fakeUploadPercentage }}%</span><el-progress :text-inside="true" :stroke-width="26" :percentage="fakeUploadPercentage" /><div class="el-upload__tip text-red">限制一个文件, 新文件将会覆盖原文件</div></template></el-upload></div>
</template>

2.逻辑代码

<script setup lang="ts" name="menu222">
import { ElMessage } from 'element-plus'
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
import { Upload } from "@/api/interface";
import { UploadStatusEnum } from "@/enums/uploadEnum";
import { SIZE } from "@/config/config";
import { bigUploadRequest } from "@/api/modules/upload";const upload = ref<UploadInstance>()
// 当前的请求xhr组成的数组
const requestListArr = ref<XMLHttpRequest[]>([])
// 组装的filechunk分段文件
let data = ref<Upload.data[]>([])
const status = ref<string>(UploadStatusEnum.wait)
// 生成文件hash的进度
const hashPercentage = ref(0)
// 显示在页面上的文件上传进度
const fakeUploadPercentage = ref(0)
// 定义上传文件的容器
const container = reactive<Upload.Container>({file: {name: '',percentage: 0,status: UploadStatusEnum.ready,size: 0,url: undefined,raw: undefined,uid: 0},hash: '',worker: null,
})// 计算:文件上传的进度
const uploadPercentage = computed({get() {if (!container.file || !data.value.length) return 0const loaded = data.value.map(item => item.size * item.percentage).reduce((acc, cur) => { return acc + cur })console.log('loaded', loaded);return parseInt((loaded / container.file.size!).toFixed(2))},set(value) {return value}
})// 计算:上传按钮是否可以点击
const uploadDisabled = computed(() => {const disabledStatus: string[] = [UploadStatusEnum.pause, UploadStatusEnum.uploading]return (!container.file || disabledStatus.includes(status.value))
})// watch uploadPercentage,得到fakeUploadPercentage
// 至于为什么要这么做,看【恢复上传】的注释
watch(uploadPercentage, (newValue) => {if (newValue > fakeUploadPercentage.value) {fakeUploadPercentage.value = newValue}
})// 选择了文件
const handleFileChange: UploadProps['onChange'] = (uploadFile, uploadFiles) => {resetData()if (!uploadFile) returncontainer.file = uploadFile
}// 上传
const handlerUpload = async () => {if (!container.file.raw) return// 点了上传按钮,状态改为上传中...status.value = UploadStatusEnum.uploading// 文件分片const fileChunkList = createFileChunk(container.file.raw)console.log('文件分了多少片:', fileChunkList.length)// 通过webworker计算出,文件hashcontainer.hash = await calculateHash(fileChunkList)console.log('文件hash是:', container.hash)// uploadedList已上传的切片的切片文件名称const { shouldUpload, uploadedList } = await verifyUpload(container.file.name,container.hash)// 组装的filechunk数据先置空data.value = []// 服务器已经有完整文件了if (!shouldUpload) {fakeUploadPercentage.value = 100status.value = UploadStatusEnum.waitawait nextTick()return ElMessage.success('秒传:上传成功')}data.value = fileChunkList.map(({ file }, index) => ({fileHash: container.hash,index,hash: `${container.hash}-${index}`,chunk: file,size: file.size,// 如果已上传切片数组uploadedList中包含这个切片,则证明这个切片之前已经上传成功了,进度设为100。percentage: uploadedList.includes(index.toString()) ? 100 : 0,}))console.log('数组', data);uploadChunks(uploadedList)
}// 暂停
const handlePause = () => {status.value = UploadStatusEnum.pauserequestListArr.value.forEach((xhr) => xhr?.abort())requestListArr.value = []if (container.worker) {container.worker.onmessage = null}
}// 重置
const resetData = () => {hashPercentage.value = 0uploadPercentage.value = 0fakeUploadPercentage.value = 0requestListArr.value.forEach(xhr => xhr?.abort())requestListArr.value = []if (container.worker) {container.worker.onmessage = null}
}/*** 【恢复上传】* 上传进度是实时根据所有的上传切片的进度汇总来的* 只有某个切片完整/全部上传到了服务端,才算这个切片上传完成了* 如果,一些切片如果只上传了一部分,就被暂停了,那么恢复上传时,这一些切片是需要重新上传的* 这样就会导致恢复上传时,上传进度倒退的问题(因为上传进度是计算属性,是实时计算切片,汇总而来的)*/
const handleResume = async () => {status.value = UploadStatusEnum.uploadingconst { uploadedList } = await verifyUpload(container.file.name,container.hash)uploadChunks(uploadedList)
}/*** @description: 生成文件切片* @param {*} file 上传的文件* @param {*} size  文件大小* @return {*}*/
const createFileChunk = (file: UploadRawFile, size = SIZE) => {const fileChunkList = []let cur = 0while (cur < file.size) {fileChunkList.push({file: file.slice(cur, cur + size),})cur += size}return fileChunkList
}/*** @description: 根据文件内容生成hash,而不是根据文件名称生成hash。* @description: 考虑到如果上传一个超大文件,读取文件内容计算 hash 是非常耗费时间的,并且会引起 UI 的阻塞,* @description: 导致页面假死状态,所以我们使用 web-worker 在 worker 线程计算 hash,这样用户仍可以在主界面正常的交互* @param {*} fileChunkList 切片数组* @return {*}*/
const calculateHash = (fileChunkList: Upload.FileChunk[]) => {return new Promise<string>((resolve) => {// 开启workercontainer.worker = new Worker('/hash.js')// 向worker线程传入参数(注意传入的是对象,使用了解构写法)container.worker.postMessage({ fileChunkList })// 接受来自worker线程的 加工后的回复container.worker.onmessage = (e: any) => {// console.log('calculateHash_Worker接收的参数', e);const { percentage, hash } = e.datahashPercentage.value = percentage.toFixed(2)// 若得到哈希值,则resolve返回if (hash) {resolve(hash)}}})
}// https://blog.51cto.com/u_15091669/2608437  xhr对象POST请求、xhr兼容性、timeout、progress
/*** @description: 上传切片进度的回调函数,用闭包保存每个chunk的进度数据* @param {*} index 切片的索引* @param {*} item 每个切片* @return {*}*/
const createProgressHandler = (index: number, item: Upload.data) => {return (e: ProgressEvent) => {if (e.lengthComputable) {item.percentage = parseInt(String((e.loaded / e.total) * 100))}}
}/*** @description: 验证该文件是否需要上次,文件通过hash生成唯一,改名后也是不需要再上传的,也就相当于妙传* @param {*} filename 文件名* @param {*} fileHash 文件哈希值* @return {*}*/
const verifyUpload = async (filename: string, fileHash: string) => {const { result } = await bigUploadRequest({url: 'http://localhost:9999/verify',method: 'post',headers: { 'content-type': 'application/json' },data: JSON.stringify({filename,fileHash,}),})return result
}/*** @description: 上传切片,同时过滤已上传的切片* @param {*} uploadedList 已经上传了的切片,这次不用上传了* @return {*}*/
const uploadChunks = async (uploadedList: string[] = []) => {const requestList = data.value.filter(({ hash }) => !uploadedList.includes(hash)).map(({ chunk, hash, index }) => {const formData = new FormData()// 切片文件formData.append('chunk', chunk)// 切片文件hashformData.append('hash', hash)// 大文件的文件名formData.append('filename', container.file.name)// 大文件hashformData.append('fileHash', container.hash)return { formData, index }}).map(async ({ formData, index }) =>bigUploadRequest({url: 'http://localhost:9999',data: formData,onProgress: createProgressHandler(index, data.value[index]),requestList: requestListArr.value,}))// 并发切片await Promise.all(requestList)// 之前上传的切片数量 + 本次上传的切片数量 = 所有切片数量时// 切片并发上传完以后,发个请求告诉后端:合并切片if (uploadedList.length + requestList.length === data.value.length) {mergeRequest()}
}// 发请求通知服务器,合并切片
const mergeRequest = async () => {await bigUploadRequest({url: 'http://localhost:9999/merge',headers: {'content-type': 'application/json',},data: JSON.stringify({size: SIZE,fileHash: container.hash,filename: container.file.name,}),})ElMessage.success('上传成功')status.value = UploadStatusEnum.wait
}</script>

3.单独的大文件上传请求

import { Upload } from "@/api/interface/index";
// * 大文件上传的单独的request
export function bigUploadRequest({ url, method = 'post', data, headers = {}, onProgress = (e: any) => e, requestList }: Upload.specialRequest): Promise<Upload.verifyUpload> {return new Promise((resolve) => {const xhr = new XMLHttpRequest()// 一个无符号长整型(unsigned long)数字,表示该请求的最大请求时间(毫秒),若超出该时间,请求会自动终止。// xhr.timeout = 100000xhr.upload.onprogress = onProgressxhr.open(method, url)Object.keys(headers).forEach((key) =>xhr.setRequestHeader(key, headers[key]))xhr.ontimeout = (e: ProgressEvent) => {console.log('请求超时')}xhr.send(data)// XMLHttpRequest请求成功完成时触发;xhr.onload = (e: ProgressEvent) => {// 将请求成功的 xhr 从列表中删除if (requestList) {const xhrIndex = requestList.findIndex((item) => item === xhr)requestList.splice(xhrIndex, 1)}let target = <XMLHttpRequest>e.targetlet result = JSON.parse(target.response)resolve({result})}// 当请求结束时触发, 无论请求成功(load)还是失败(abort 或 error)。也可以使用 onloadend 属性。xhr.onloadend = (e) => e// 暴露当前 xhr 给外部requestList?.push(xhr)})

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

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

相关文章

30道JVM综合面试题详解含答案(值得珍藏)

1. 描述一下JVM加载Class文件的原理机制? Java中的所有类&#xff0c;都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类&#xff0c;而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候&#xff0c;我们几乎不需要关心类的加载&#xff0c;因为这些都…

网络编程的理论基础

文章目录 1 重点知识2 应用层3 再谈 "协议"4 HTTP协议4.1 认识URL4.2 urlencode和urldecode4.3 HTTP协议格式4.4 HTTP的方法4.5 HTTP的状态码4.6 HTTP常见Header4.7 最简单的HTTP服务器 3 传输层4 再谈端口号4.1 端口号范围划分4.2 认识知名端口号(Well-Know Port Nu…

环信IM Demo登录方式如何修改为自己项目的?

在环信即时通讯云IM 官网下载Demo&#xff0c;本地运行只有手机验证码的方式登录&#xff1f;怎么更改为自己项目的Appkey和用户去进行登录呢&#xff1f; &#x1f447;&#x1f447;&#x1f447;本文以Web端为例&#xff0c;教大家如何更改代码来实现 1、 VUE2 Demo vue2…

叉车车载终端定制_基于MT6762安卓核心板的车载终端设备方案

叉车车载终端是一款专为叉车车载场景设计的4英寸Android车载平板电脑。它采用了高能低耗的8核ARM架构处理器和交互开放的Android 12操作系统&#xff0c;算力表现强大。此外&#xff0c;该产品还具备丰富的Wi-Fi-5、4G LTE和蓝牙等通讯功能&#xff0c;可选配外部车载蘑菇天线&…

【麒麟V10系统x86环境--bash: ./install:/bin/bash:解释器错误: 权限不够】

不知道那位大拿分享的这个神操作、给力呀 标题-bash: ./install&#xff1a;/bin/bash&#xff1a;解释器错误: 权限不够 执行这个命令即可&#xff1b;sudo setstatus Softmode

Ubuntu server搭建dhcp服务器

安装 直接使用一下命令进行安装 apt-get install isc-dhcp-server 以下就是安装好的图片 然后进入dhcp目录 cd /etc/dhcp 进入后用ls查看当前目录存在哪些文件 使用如下进入dhcp.conf vim dhcpd.conf 红&#xff1a;设置ip域和子网掩码 绿&#xff1a;设置ip池范围 黄…

t2vec code

文章目录 执行过程preprocess.jl 解释h5 文件结构 执行过程 (base) zzqserver1:~/project/t2vec/preprocessing$ julia porto2h5.jl Processing 1710660 trips… 100000 200000 300000 400000 500000 600000 700000 800000 900000 1000000 1100000 1200000 1300000 1400000 15…

2024最新适用于 Windows 、Mac 的最佳屏幕录制软件

屏幕录制软件可以帮助我们录制 PC 和MacBook的实时屏幕视频。如果您想为 优酷录制视频&#xff0c;或者您正在为您的公司制作基于视频的项目&#xff0c;并且需要捕获屏幕的实时视频录制&#xff0c;那么我们在此列出了 一 款适合您的 Windows 、Mac的 2024 年最佳屏幕录制软件…

锤科HandShaker修改版,支持安卓14、澎湃OS

如今几乎各家手机厂商都在布局生态&#xff0c;但PC端往往是最容易被忽略的一环&#xff0c;哪怕是很强的华为鸿蒙、小米澎湃&#xff0c;想要做到手机和电脑互联&#xff0c;也限制了笔记本机型 虽然我一直致力于解锁非小米电脑安装小米电脑管家&#xff0c;比如前几天刚刚更…

决策树(公式推导+举例应用)

文章目录 引言决策树学习基本思路划分选择信息熵信息增益增益率&#xff08;C4.5&#xff09;基尼指数&#xff08;CART&#xff09; 剪枝处理预剪枝&#xff08;逐步构建决策树&#xff09;后剪枝&#xff08;先构建决策树再剪枝&#xff09; 连续值与缺失值处理连续值处理缺失…

考古学家 - 华为OD统一考试

OD统一考试 分值&#xff1a; 200分 题解&#xff1a; Java / Python / C 题目描述 有一个考古学家发现一个石碑&#xff0c;但是很可惜发现时其已经断成多段。 原地发现N个断口整齐的石碑碎片&#xff0c;为了破解石碑内容&#xff0c;考古学家希望有程序能帮忙计算复原后的石…

精品公式——“V型反转”,精准把握V型反转行情,主副图分享

► 日线表现 代码评估 技术指标代码评估&#xff1a; M5, M14, M25 - 指数移动平均线&#xff08;EMA&#xff09;: M5:EMA(C,5),COLORLIBLUE;&#xff1a;5日指数移动平均线&#xff0c;用浅蓝色表示。 M14:EMA(C,13),COLORF00FF0;&#xff1a;13日指数移动平均线&#xff…

定时任务框架-xxljob

spring传统的定时任务Scheduled&#xff0c;但是这样存在这一些问题 &#xff1a; 做集群任务的重复执行问题 cron表达式定义在代码之中&#xff0c;修改不方便 定时任务失败了&#xff0c;无法重试也没有统计 如果任务量过大&#xff0c;不能有效的分片执行 1.分布式任务调…

详解矩阵的正交化(附例题分析)

目录 一. 矩阵Gram-Schmidt正交化的好处 二. 矩阵标准正交化过程 三. 例题 3.1 标准正交化 3.2 算法小结 3.3 优化分析 四. 小结 矩阵有两类等价关系 矩阵对角化 特殊矩阵 一. 矩阵Gram-Schmidt正交化的好处 假如有三个线性独立的向量a,b,c&#xff0c;他们是标准正…

Word不同部分(分节)设置页眉和页码的使用指南——附案例操作

Word页眉和页码分节设置的使用指南 目录 Word页眉和页码分节设置的使用指南摘要1. 插入分节符2. 设置不同的页眉3. 设置不同的页码4. 调整页码的起始值5. 删除或更改分节6. 预览和调整 摘要 在撰写word文档时&#xff0c;我们经常需要在不同的部分应用不同的页眉和页码格式。在…

2024年 最新 iPhone手机 历代机型、屏幕尺寸、纵横比、分辨率 整理

&#x1f3ac; 博客主页&#xff1a;https://xiaoy.blog.csdn.net &#x1f3a5; 本文由 呆呆敲代码的小Y 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;Unity系统学习专栏 &#x1f332; 游戏制作专栏推荐&#xff1a;游戏制作 &…

数字信号处理 唐向宏著 pdf +课后答案 免费下载

数字信号处理——原理、实现与仿真 pdf 唐向宏著 &#xff0b;课后答案 杭州电子科技大学 费劲心思在网上花钱买的&#xff0c;共享给大家 永久链接&#xff1a;https://wwi.lanzoup.com/b0140pf4f 密码&#xff1a;aflj 里面除了有原书PDF&#xff0c;还有课后题答案

启动redis出现Creating Server TCP listening socket 127.0.0.1:6379: bind: No error异常

1.进入redis安装目录&#xff0c;地址栏输入cmd 2.输入命令 redis-server.exe redis.windows.conf redis启动失败 解决&#xff0c;输入命令 #第一步 redis-cli.exe#第二步 shutdown#第三步 exit第四步 redis-server.exe redis.windows.conf 显示以下图标即成功

日程安排小程序实战教程

日常中我们经常有一些事情需要提醒自己&#xff0c;使用日历的形式比较符合实际的使用习惯。本篇我们就利用微搭低代码工具带着大家开发一款日程安排的小程序。 1 创建数据源 登录微搭低代码控制台&#xff0c;打开数据模型&#xff0c;点击创建 输入数据源的名称日程安排 …

【PaperReading- VLM】1. FERRET

CategoryContent论文题目FERRET: REFER AND GROUND ANYTHING ANYWHERE AT ANY GRANULARITY作者Haoxuan You (Columbia University), Haotian Zhang, Zhe Gan, Xianzhi Du, Bowen Zhang, Zirui Wang, Liangliang Cao (Apple AI/ML), Shih-Fu Chang (Columbia University), Yinfe…