为什么要分片
在资源上传的时候,如果文件过大,不仅会使上传速度非常慢,而且万一出现上传过程中断网的问题,整个文件都需要重传,这个代价是很大的,用户体验感会很差,这时就需要分片上传
如何切片
File对象有一个slice方法,接收两个参数,切片起始位置和切片结束位置(左闭右开),使用和数组的slice方法一样
//假设有一个文件file
const piece=file.slice(0,100)
console.log(piece)
打印出来发现分片的结果是Blob类型。Blob类型和File类型一样,都可以直接上传给服务器。
于是我们就可以写出一个分片的方法
let chunks=createChunks(file,10*1024*1024)//切片大小为10M
function createChunks(file,chunkSize){
const result=[]
fot(let i=0;i<file.size;i+=chunkSize){
result.push(file(i,i+chunkSize))
}
return result
}
注意:这里分片分的是file对象,file对象只是一个存储资源的大小,名字等的对象,所以分片速度会很快。上传资源的时候,我们上传file对象,服务器根据file对象找到资源才进行上传,所以上传Blob就是服务器找到资源对应的某一块进行上传。
如果上传中断,如何从中断的地方继续上传
这里需要客户端向服务器发送请求,询问这个文件上传过吗,我应该从哪开始继续上传。服务器就会返回上次中断的地方使客户端继续上传
服务器怎么知道这个文件:如果文件过大,客户端不可能拿到完整资源去询问服务器,于是就需要一个值去代表它——hash值。在第一次上传的时候就传给服务器这个文件的hash值,如果文件上传中断,客户端就携带这个hash值去询问服务器
文件hash计算
文件计算hash可以使用第三方库来进行计算。计算需要根据整个资源去计算,所以客户端缓存整个资源计算hash显然是不太好的,既然上面已经进行了分片,那么hash计算就采用:根据分片计算hash,然后再将分片的hash与下一个分片的hash合并以计算整个资源的hash
const result=await hash(chunks)
function hash(){
return new Promise(resolve=>{
const spark=new SparkMD5()//计算hash的第三方库
function read(i){
resolve(speak.end)//输出的最终结果
if(i>=chunks.length){
return
}
const blob=chunks[i];
const reader=new FileReader()
reader.onload=e=>{
const bytes=e.target.result//读取到的字节数组
speak.append(bytes)
read(i+1)
}
//读的过程是异步的
reader.readArrayBuffer()
read(0)
})
}
整个hash的计算过程还是很大的,所以我们最好把它写成一个异步的,或者使用webWorker去给他再开一个线程去处理,避免浏览器卡死