最新公告
  • 欢迎您光临网站无忧模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端大文件上传,断点续传(附源码)

    正文概述 掘金(聪明可爱小轩轩_cuteXuan)   2021-02-28   991

    前言

    无论是面试还是实际工作,我们都会遇到大文件上传的问题。事实上,在我之前的面试中,也被问到上传大文件(Excel)如何处理,当时答的磕磕巴巴,直接导致整个面试以失败结束。 最近想起了整个事情,花了点时间,整理出这个demo,当然了,整篇文章,不仅仅讲思路过程,最后也会附送源码

    前端:Vue.js Element-Ui

    后端:node.js express fs

    思路

    前端
    大文件上传
    • 将大文件转换成二进制流的格式
    • 利用流可以切割的属性,将二进制流切割成多份
    • 组装和分割块同等数量的请求块,并行或串行的形式发出请求
    • 待我们监听到所有请求都成功发出去以后,再给服务端发出一个合并的信号
    断点续传
    • 为每一个文件切割块添加不同的标识
    • 当上传成功的之后,记录上传成功的标识
    • 当我们暂停或者发送失败后,可以重新发送没有上传成功的切割文件
    后端
    • 接收每一个切割文件,并在接收成功后,存到指定位置,并告诉前端接收成功
    • 收到合并信号,将所有的切割文件排序,合并,生成最终的大文件,然后删除切割小文件,并告知前端大文件的地址

    其实说到这里,如果你看懂并且理解了以上的思路,那么你已经学会了大文件上传+断点续传的 80%。下面的具体实现过程,对你来讲,就是小意思...

    大文件上传代码部分

    本次html部分,我们使用了 Element-Ui 的组件,代码很容易理解

    <el-upload
          drag
          action
          :auto-upload="false" 
          :show-file-list="false" 
          :on-change="changeFile"
          >
          <i class="el-icon-upload"></i>
          <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
    </el-upload>
    

    js部分的逻辑,按照我们的上面的分析,我们可以写出如下的结构

    methods: {
        // 提交文件后触发
        changeFile() {
            this.filepParse()
    
            // coding... 进行分片
            // ...
            
            // 创建切片请求
            this.createSendQeq()
            this.sendQeq()
            this.mergeUpload
        },
        // 将文件变成二进制,方便后续分片
        filepParse() {
        },
        // 创建切片请求
        createSendQeq() {
        },
        // 将每一个切片 并行/串行 的方式发出
        sendQeq() {
        },
        // 发送代码合并请求
        mergeUpload() {
        }
      }
    

    按照上面的代码,写出这样的结构还是很容易的,接下来要做的就是补全这些逻辑

    将文件变成二进制,方便后续分片

    js常见的二进制格式有 Blob,ArrayBuffer和Buffe,这里没有采用其他文章常用的Blob,而是采用了ArrayBuffer,如果你对二进制流不了解,欢迎加我关注我的公众号【聪明可爱小轩轩】加我私人微信,到时候我们专门写一篇文章。 又因为我们解析过程比较久,所以我们采用 promise,异步处理的方式

    filepParse(file, type) {
      const caseType = {
        'base64': 'readAsDataURL',
        'buffer': 'readAsArrayBuffer'
      }
      const fileRead = new FileReader()
      return new Promise(resolve => {
        fileRead[caseType[type]](file)
        fileRead.onload = (res) => {
          resolve(res.target.result)
        }
      })
    }
    
    将大文件进行分片

    在我们拿到具体的二进制流之后我们就可以进行分块了,就像操作数组一样方便。

    当然了,我们在拆分切片大文件的时候,还要考虑大文件的合并,所以我们的拆分必须有规律,比如 1-1,1-2,1-3 ,1-5 这样的,到时候服务端拿到切片数据,当接收到合并信号当时候,就可以将这些切片排序合并了。

    同时,我们为了避免同一个文件(改名字)多次上传,我们引入了 spark-md5 ,根据具体文件内容,生成hash值

    const buffer = await this.filepParse(file,'buffer')
          
    const sparkMD5 = new SparkMD5.ArrayBuffer()
    
    sparkMD5.append(buffer)
    this.hash = sparkMD5.end()
    

    而我们,为每一个切片命名当时候,也改成了 hash-1,hash-2 这种形式,

    我们分割大文件的时候,可以采用 定切片数量,定切片大小,两种方式,我们这里采用了 定切片数量这个简单的方式做例子

    const partSize = file.size / 10
    let current = 0
    
     for (let i = 0 ;i < 10 ;i++) {
       let reqItem = {
         chunk: file.slice(current, current + partSize),
         filename: `${this.hash}_${i}.${suffix}`
       }
       current += partSize
       partList.push(reqItem)
     }
     this.partList = partList
    

    当我们采用定切片数量的方式,将我们大文件切割完成,并将切割后的数据存给一个数组变量,接下来,就可以封装的切片请求了

    创建切片请求

    这里需要注意的就是,我们发出去的数据采用的是FormData数据格式,至于为什么大家可以找资料查询。如果依旧不太明白,老规矩,欢迎加我关注我的公众号【聪明可爱小轩轩】加我私人微信,到时候我们专门写一篇文章说明。

    createSendQeq() {
        const reqPartList = []
        this.partList.forEach((item,index) => {
          const reqFn = () => {
            const formData = new FormData();
            formData.append("chunk", item.chunk);
            formData.append("filename", item.filename);
            return axios.post("/upload",formData,{
              headers: {"Content-Type": "multipart/form-data"}
            }).then(res => {
              console.log(res)
            })
          }
          reqPartList.push(reqFn)
        })
        return reqPartList
    }
    
    将每一个切片 并行/串行 的方式发出

    目前切片已经分好了,并且我们的请求也已经包装好了。 目前我们有两个方案 并行/串行 因为串行容易理解,这里拿串行举例子。

    我们每成功的发出去一个请求,那么我们对应的下标就加一,证明我们的发送成功。当 i 下标和 我们的切片数相同的时候,我们默认发送成功,触发 合并(merge)请求

    sendQeq() {
     const reqPartList = this.createSendQeq()
      let i  = 0 
      let send = async () => {
        if (i >= reqPartList.length) { 
          // 上传完成
          this.mergeUpload()
          return 
        }
        await reqPartList[i]()
        i++
        send()
      }
      send()
      
    }
    

    当然了,并行发送的最大缺点就是没有串行快,但胜在代码简单,容易理解代码

    断点续传代码部分

    理解了前面的思路,这一部分的代码也容易得到思路 点击暂停的时候,停止上传。点击继续上传的话,我们继续上传剩下的请求,所以,我们对上传成功的请求要做处理

    if (res.data.code === 0) {
        this.count += 1;
        // 传完的切片我们把它移除掉
        this.partList.splice(index, 1);
    }
    

    如果上传成功,我们就将其从请求数组中剥离出去,这样我们就可以保证剩下的,就是待上传的了,断点续传的核心就是如此,其他是一些业务了

    至于服务端的代码,这里就不具体说明了,只要是看懂了整体的思路,看服务端代码还是很容易理解的。如果有问题,欢迎关注公众号【聪明可爱小轩轩】加我私人微信,我们一起学习进步。当然了,如果想要拿到代码的,回复关键字bigFile即可...

    下面,让我们再次分析一下我们的这个大文件上传呗

    问题总结

    当前的例子,只是提供一个简单的思路,比如:

    • 某个切片上传失败怎么办?
    • 如何进行并行上传(更快)
    • 当前是规定了切片的数量,改成定切片大小如何处理
    • 上传过程中刷新页面怎么办
    • 大文件上传如何结合 Web Work 处理大文件上传
    • 如何实现秒传

    诸如此类,这个例子还有很多需要我们完善的地方,等后续时间宽裕,我会出一个加强版的大文件上传

    如果你感觉这篇文章对你有帮助,欢迎关注公众号【聪明可爱小轩轩】


    下载网 » 前端大文件上传,断点续传(附源码)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元