文件切片及断点续传
一、初衷、想法
今年上半年的时候写了一个文件切片的库(凭空想象写的,没有结合实际项目开发),近期在使用的时候发现有些功能并没有考虑周全,然鹅花了这周六日来重写?
之前的思路是,传入文件及对应的fileKey,然后根据byte来进行切片,切一片发送一片,直到全部发送完毕,执行callback,当时只考虑到了单个文件切片上传。
现在的思路,先全部切片完后再上传,对比后端返回的当前文件还有哪些切片未被上传(实现断点续传),多个文件上传,如果单个切片上传失败单个切片自动进行三次上传请求,所有文件全部上传成功后,再执行callback。over。
其实文件切片及断点续传并不难,最重要的是有思路,然后将自己的思路用代码实现。over。(编程中的任何事情都一样,最重要的是要有思路。)
这篇博客只会抛出代码及部分重要注释,拒绝做复制侠,里面的逻辑并不复杂。
二、后端需要提供的两个接口
2.1 查询当前文件是否已经存在
对前端用户而已来说:提升用户体验、加快文件传输速度
对后端服务来说:减少不必要的带宽占用和磁盘空间的浪费
如果已经存在就不再继续上传该文件,如何判断当前文件是否已存在。唯一id: md5
这里的md5转成了62进制,因为16进制比较长。
2.2 上传文件
将切片文件上传给服务器,服务器进行合成。
三、生成md5+文件切片库
import Vue from 'vue';
import SparkMD5 from 'spark-md5'
// 思路
/**
* 首先将对传入的文件做MD5加密,根据加密后的MD5查询这个文件是否已被上传过,
* 查询这个文件是否“部分”切片被上传,还有哪些切片未被上传,对文件进行切片(根据大小或数量切片)
* 返回切片数组
*/
class SectionFileNew {
// 文件md5加密
getFileMD5 = (file: File, callback?: Function) => {
const spark = new (SparkMD5 as any)(),
fileReader = new FileReader();
const ops = () => new Promise(resolve => {
fileReader.readAsBinaryString(file)
fileReader.onload = function (e: any) {
spark.appendBinary(e.target.result)
const md5key = spark.end()
resolve(md5key)
callback && callback(md5key)
}
})
async function invoke() {
return await ops()
}
return invoke()
}
// 16进制转62进制
string16to62 = (val: any) => {
val = parseInt(val, 16)
let chars = '0123456789abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ'.split(''),
radix = chars.length,
qutient = +val,
arr = [];
do {
const mod = qutient % radix;
qutient = (qutient - mod) / radix;
arr.unshift(chars[mod]);
} while (qutient);
return arr.join('');
}
// 文件切片
/**
* chunkByteSize 字节 默认0.5兆
*/
sectionFile = async (file: File, callback: Function, chunkByteSize = 524288, callbackFileType = "file") => {
chunkByteSize = chunkByteSize ? chunkByteSize : 524288;
// 计算该文件的可分为多少块
const chunks: any = Math.ceil(file.size / chunkByteSize);
let sectionFileArray = []
if (chunks === 1) {
sectionFileArray.push(file)
return callback && callback(sectionFileArray, chunks, this.string16to62(await this.getFileMD5(file)))
}
// 当前切片
for (let i = 0; i < chunks; i++) {
const start = i * chunkByteSize,
end = Math.min(start + chunkByteSize, file.size)
const blob = file.slice(start, end);
// blob 转 file
let res = callbackFileType === "file" ? new window.File([blob], file.name, { type: file.type }) : blob;
sectionFileArray.push(res)
}
callback && callback(sectionFileArray, chunks, this.string16to62(await this.getFileMD5(file)))
}
}
export default function () {
Vue.prototype.$sectionFileNew = SectionFileNew
}
export const sectionFileFnNew = SectionFileNew;
四、结合项目封装库
import Vue from "vue"
import { sectionFileFnNew } from "./indexNew"
import { sectionToUpload, checkHash } from "@/api/http/sectionToUpload";
export default function () {
class ProjectFileUploadNew {
private section = new (sectionFileFnNew as any)()
constructor() { }
/**
* 项目级单个文件上传
* file:文件
* fileKey:对应上传的key
* callback:回调
*/
projectFileSection = async (file: File, fileKey: string, callback?: Function): Promise
发表评论 (审核通过后显示评论):