博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
大文件断点续传/切片上传(react、vue通用)
阅读量:3961 次
发布时间:2019-05-24

本文共 5816 字,大约阅读时间需要 19 分钟。

----整体流程解析 - 1

断点续传首先要满足的功能是第一次上传30%关闭上传后,再次上传此文件继续从30%开始继续上传。
要想满足此功能,首先要做的是把一个文件切割成多份上传,而每次上传时,后台需要告诉你此文件已经上传了多少份。
例:A文件(100M),切割成100份,每一份有一个id,如1-100,第一次上传了id1-30的切片文件总计三十份,打断上传后,第二次上传时后台先告诉你已上传id1-30,前端知道后,直接从id31开始上传,做到了续传的实现。

----整体流程解析 - 2

功能实现至少需要后台提供三个接口。
1 - 注册上传任务接口
此接口需要传给后台需要上传的文件的md5,并且针对这个文件开启一个上传任务,后台根据你传的文件的md5判断此文件有没有上传过,如果上传过需要告诉你此文件已经上传了几份(切片id数组),你根据此数组过滤出已经上传过的切片,只上传没上传过的切片。
2 - 上传文件切片接口
此接口需要轮巡多次调用,把文件切片传给后台
3 - 上传结束,文件合并接口
切片接口调用结束后

----上代码

第一步拿到file,我在这里是直接用的Ant Design的Upload组件,也可以直接用input,只要能拿到file就可以

// 一 、第一个函数(file前置处理函数)customRequest: async (params: {
file }) => {
setLoading(true) // 开启页面loading,避免切片大文件期间用户乱点,每个项目开启loading的api不同,不一定是setLoading const {
file } = params // 拿到file const {
size, type, name } = file // 解构出可能用到的几个属性 // 1.格式判断(types是props参数,是支持的格式数组,例:types: ['jpg', 'png', 'bmp', 'jpeg']),如果不需要做格式限制,可以不写第一条的代码 if (types.length !== 0) {
const fileExt = /\.([0-9a-zA-Z]+)$/i.exec(name)[1] // 获取后缀 const reg = new RegExp(`(${
types.join('|')})$`) if (!reg.test(type) && !reg.test(fileExt)) {
setLoading(false) // 关闭loading message.error(messageError) // 格式不对提示页面提示,提示语messageError也是props传进来的参数,也可以直接写死几个字如“格式不正确” return false } } // 2.大文件判断(maxSize是props参数,让用户自定义限制最大上传文件大小) if (size > maxSize * 1024 * 1024) {
setLoading(false) // 关闭loading message.error('文件格式或大小不符合要求,请重新上传!') return false } // 3.获取文件md5值(文件的md5值是根据文件内容生成的一个单独的标示,需要安装并引入一个插件:import SparkMD5 from 'spark-md5') const result: any = await fileParse(file, 'buffer') // fileParse转换函数我附在最下面👇,将file转成buffer格式数据, const spark = new SparkMD5.ArrayBuffer() spark.append(result) const hash = spark.end() // 获取到的文件md5值 // 4.重复文件判断 /** 如果重复直接提示并return,此部分代码根据框架不同自己写,如果不需要做去重可不写,这里就不贴出来了…… **/ // 5.文件切片上传 const partSize = 5 * 1024 * 1024 // 考虑到前后端上传效率,我这里是切成5M/片,这个可根据项目实际需要自己决定 const share = size <= partSize ? 1 : size / partSize // 判断如果文件<=5M,不切片,超过按照5M每份切 let cur = 0 const partList = [] // 存储文件切片的数组 for (let i = 0; i < share; i++) {
const obj = {
file: file.slice(cur, cur + partSize), // 调用file.slice方法进行file的切片,第一个参数从哪里开始切,第二个参数切到哪里 id: i + 1 // 每一个切片的单独标示,后期续传时用与去除已上传部分的切片 } partList.push(obj) // 把切的每一片统一放到一个数组里 cur += partSize } isOpen = true // isOpen代表上传开关,true标示打开上传开关,false标示关闭上传开关,你可以定义不同的名字,并且在哪里定义看你所用的框架 setLoading(false) // 关闭loading // 6.调用断点续传 register(partList, hash, name) // 此函数往下看👇 }, // 二 、 第二个函数(注册上传任务函数 -- 调用第一个接口,注册上传任务接口) const register = (partList, hash, fileName) => {
// 1.调第一个接口,上传任务注册接口,参数需要将文件的md5传过去,后台专门根据此文件开启一个上传任务(一个文件对应一个上传任务) taskRegistration({
fileMd5: hash }) .then((res) => {
const {
uploadId, status, partNumSet } = res.data // uploadId是与这个文件对应的上传任务的id标示,status是此文件的状态,如已上传部分切片||从未进行上传,这是第一次||已经上传完了还未进行合并,partNumSet是一个数组,里面放着已经上传的切片的id,如果是从未进行上传,就是空数组 const mergeParame = {
hash, uploadId, fileName } if (status === 1) {
// 1.已全部上传,未合并,直接调合并函数 complete(mergeParame) } else if (status === 2) {
// 2.已上传部分,根据后台返回的partNumSet字段(已上传切片id的数组)筛选,去除已上传部分,把剩下的切片组成一个新数组,如果筛选出来的新数组长度是0说明切片都已上传,直接合并,否则调用上传切片函数 const newPartList = [] partList.forEach((item) => {
if (partNumSet.indexOf(`${
item.id}`) === -1) {
newPartList.push(item) } }) if (newPartList.length === 0) {
complete(mergeParame) } else {
newRequestArr(newPartList, hash, fileExt, uploadId, mergeParame) } } else if (status === 3) {
// 3.从未上传过此文件,调用上传切片函数 newRequestArr(partList, hash, fileExt, uploadId, mergeParame) } }) } // 三 、 第三个函数(上传切片函数 -- 调用第二个接口,上传文件切片接口) const newRequestArr = (partList, hash, fileExt, uploadId, mergeParame) => {
let i = 0 const fn = () => {
const formData = new FormData() // 通过form将file切片传给后台 formData.append('file', partList[i].file) formData.append('fileMd5', hash) formData.append('fileExt', fileExt) formData.append('uploadId', uploadId) formData.append('partNumber', partList[i].id) sliceUpload(formData).then(() => {
if (isOpen.current) {
// 如果上传开关在这里是false,则会停止递归调用上传函数,至于在哪里把他置为false,看你在哪里点击触发的暂停或者删除上传函数了,自己决定 i += 1 if (i < partList.length) {
fn() } else {
complete(mergeParame) // 如果是最后一个切片,则调用合并函数 } } }) } fn() } // 四 、 第四个函数(文件合并函数 -- 调用第三个接口,文件合并接口) const complete = (mergeParame) => {
fileMerge(mergeParame).then((res) => {
const {
status, fileId } = res.data if (status === 1) {
message.error('上传失败,请重试!') return } if (status === 2) {
message.error('有其它用户正在上传该文件,请稍后重试!') return } }) } // 转换函数 const fileParse = (file, type = 'base64') => {
return new Promise((res) => {
const fileReader = new FileReader() /* fileReader.readAsArrayBuffer() // 转成buffer格式数据 fileReader.readAsBinaryString() // 转成二进制格式数据 fileReader.readAsDataURL() // 转成base64格式数据 解析过程是异步,所以需要调用onload事件的e.target.result获取转换后的结果 */ switch (type) {
case 'base64': fileReader.readAsDataURL(file) break case 'buffer': fileReader.readAsArrayBuffer(file) break case 'binary': fileReader.readAsBinaryString(file) break default: break } fileReader.onload = (e) => {
res(e.target?.result) } })}

此代码只是实现了基础的断点续传,一般还需实现进度条和文件列表,这些可以根据自己使用的框架和自己的ui和需求自己写就可以了,在这里就不贴代码了,希望可以对有需要的同学起到帮助,谢谢

转载地址:http://wlqzi.baihongyu.com/

你可能感兴趣的文章
centos下nmap安装和基础命令
查看>>
ubuntu出现有线已连接却无法上网
查看>>
一句话命令
查看>>
解决Linux CentOS中cp -f 复制强制覆盖的命令无效的方法
查看>>
wdcpv3升级到v3.2后,多PHP版本共存的安装方法
查看>>
centos tar压缩与解压缩
查看>>
Centos 7防火墙firewalld/iptables开放80端口
查看>>
centos 7 yum源文件配置详解及163 yum源更换
查看>>
PHP统计当前网站的访问人数,访问信息,被多少次访问。
查看>>
Windows10远程报错CredSSP加密oracle修正
查看>>
Windows server 2016 设置多用户登陆
查看>>
偶然发现的面包屑
查看>>
每天自动升级你的Centos
查看>>
WDCP v3版本的小工具集
查看>>
CentOS 7 下挂载NTFS文件系统磁盘并设置开机自动挂载
查看>>
Mysql修改最大连接数&重启
查看>>
华为交换机划分vlan
查看>>
CentOS 6.6 搭建Zabbix 3.4.8 过程
查看>>
make: *** No targets specified and no makefile found. Stop.解决方法
查看>>
安装zabbix 3.4版本编译报错configure: error: Unable to use libevent (libevent check failed) 解决办法
查看>>