前端实现文件分片和断点续传

场景:

提示:在项目中会遇到大文件上传,使用传统二进制码传输方式,可能因为用户误操作、网络不通畅和其他各种原因导致无法一次性完成,这时用户可能又要重新上传,就浪费了时间,此场景就可以使用文件分片

涉及知识:

  1. MD5(使用spark-md5插件进行计算)
  2. ArrayBuffer操作

代码展示:

这里使用的是ant-designupload组件,大家测试也可以直接用input标签,设置typefile就可以了

  1. 组件部分
<a-upload
          name="file"
          :show-upload-list="false"
          :multiple="multiple"
          :headers="tokenHeader"
          :accept="accept"
          :beforeUpload="beforeUpload"
          :customRequest="handleUpload" //重点关注
          :data="data"
          :action="url.upload"
          @change="handleChangeEvent"
        >
          <a-button>
            <m-icon type="iconupload" />
            上传附件
          </a-button>
</a-upload>
  1. 用到的数据

首先在选择选择文件后,通过组件的自定义事件的参数获取到对应文件对象
前端实现文件分片和断点续传_第1张图片

import SparkMD5 from 'spark-md5'
data(){
	return {
		chunkList:[], //对文件进行分片处理后,储存的数组
		successChunk:0,//判断片段是否都上传成功
	}
}
  1. 对文件分片,并计算文件MD5

computeMD5 (file) {
            const _this = this
            return new Promise((resolve, reject)=>{
                try {
                    let fileReader = new FileReader()
                    let blobSlice = File.prototype.slice
                    let currentChunk = 0 // 当前第几片
                    const chunkSize = 8 * 1024 * 1024 // 每片的大小,这里是5M
                    let chunks = Math.ceil(file.size / chunkSize) // 总片数
                    let spark = new SparkMD5.ArrayBuffer()
                    let loadNext = () => {
                        let start = currentChunk * chunkSize
                        let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize
                        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
                        _this.chunkList.push(blobSlice.call(file, start, end)) // 将每次分片的字节流放到数组里
                    }
                    loadNext()
                    fileReader.onload = (e => {
                        spark.append(e.target.result) //插件计算MD5
                        currentChunk++
                        if (currentChunk < chunks) {
                            loadNext()
                        } else {
                            this.md5 = spark.end()
                            resolve(this.md5)
                        }
                    })
                    fileReader.onerror = function () {
                        this.$message.error(`文件${file.name}读取出错,请检查该文件`)
                    }
                }catch(e) {
                    reject(e)
                }
            })
        },
  1. 分片上传文件
handleUpload (param) {
            const file = param.file // 组件提供的文件
            const that = this
            this.computeMD5(file).then((md5)=>{
                // 这里请求接口,发送MD5返回是否已上传该片段
                if (this.chunkList.length > 0) {
                    axios('检查文件接口', {	//此处需要后端支持,首先检查文件是否上传完成或者是已经上传过但是未完成,这一步也是断点续传的关键
                        fileMd5: md5, //通过上面computeMD5最后计算得到
                        fileName: file.name,
                        chunkCount: this.chunkList.length
                    }).then(async res => {
                        if(res.success) {
                            if(res.noSendChunkList.length > 0) { 
                            //返回结果,看看有多少分片没有上传
                                this.chunkList = this.chunkList.filter((item, index) => {
                                //根据拿到的分片序号res.noSendChunkList对分片数组修改
                                    return res.noSendChunkList.includes(index + 1)
                                })
                                for(let i = 0, len = this.chunkList.length;i<len;i++) {
                                    let item = this.chunkList[i]
                                    let formData = new FormData() // 按分片个数发送请求,传输文件使用的是FormData
                                    formData.append('file', item)
                                    formData.append('md5', md5)
                                    formData.append('chunkSize', item.size)
                                    formData.append('chunkIndex', res.noSendChunkList[i]) // 属于第几片
                                    let data = await axios('发送分片接口', formData)
                                    if (data.success) {
                                        this.successChunk++ // 记录当前已上传成功的片数,这里可以看需求可以加个进度条
                                        if(this.chunkList.length == this.successChunk){
                                        this.merge()
                                        }
                                    } else {
                                        this.$message.warning(data.message)
                                    }
                                }
                            }else {
                                this.merge()
                            }
                              
                        }
                    })
                }
            })
        },


  1. 让后端对文件进行合并

merge(){
	//告诉后端分片已全部传输完成,可以进行分片合成,再提供相关信息
}

总结

继续加油!

你可能感兴趣的:(前端开发问题,前端,javascript,开发语言,vue.js)