终于把“前端大文件上传”知识点整理出来了,断点续传,附源码_编程-老鸟的博客-程序员秘密

技术标签: css  html5  html  css3  

前言

无论是面试还是实际工作,我们都会遇到大文件上传的问题。事实上,在我之前的面试中,也被问到上传大文件(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);
}

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

问题总结

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

  • 某个切片上传失败怎么办?
  • 如何进行并行上传(更快)
  • 当前是规定了切片的数量,改成定切片大小如何处理
  • 上传过程中刷新页面怎么办
  • 大文件上传如何结合 Web Work 处理大文件上传
  • 如何实现秒传
  • 最后  需要这些资料的,可以点击这里领取 

     

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/PC_372/article/details/114262279

智能推荐

JavaScript基础8-JavaScript 使用date  with 三元表达式 打印年月日:使用prototype 属性 ,可以给对象扩展方法;_西方契约的博客-程序员秘密

JavaScript 使用date with 三元表达式 打印年月日:Javastript 使用prototype 属性 ,可以给对象扩展方法:例如给字符串String对象扩展trim方法;JavaScript:使用prototype 编写工具类,给String 增加trim方法:在 js文件中,引用写好的工具类;字符串对象就可以使用自己扩展之后的trim方法了:代码 ...

【巨杉数据库SequoiaDB】SequoiaDB+SparkSQL 在数据统计场景的应用_�Horde�的博客-程序员秘密

前言在当前企业生产数据膨胀的时代,数据即使企业的价值所在,也是一家企业的技术挑战所在。所以在海量数据处理场景上,人们意识到单机计算能力再强也无法满足日益增长的数据处理需求,分布式才是解决该类问题的根本解决方案。而在分布式领域,有两类产品是至关重要的,分别分布式存储和分布式计算,用户只有将两者的特性充分利用,才可以真正发挥分布式架构的存储和计算能力。本文主要向读者们介绍SequoiaDB(分布...

内存管理_qq_33508006的博客-程序员秘密

1.自动引用计数ARC2.alloc用来分配内存,分配完成之后,引用计数值为1,只能调用一次   retain使引用计数+1,可使用多次   release使引用计数-1,可调用多次   当引用计数为0时,系统自动调用dealloc方法回收内存   (自定义类需要重写dealloc方法来判断对象是否需要被回收)3.野指针:指向一块已经被销毁的内存的指针   僵尸对象:所

unity3d基本的C#程序框架_BIM云平台开发的博客-程序员秘密

using System.Collections;using System.Collections.Generic;using UnityEngine;public class Follow : MonoBehaviour {// Use this for initializationvoid Start () {}// Update is called once per framevoid Up

ubuntu14.04安装afni_windows afni_selous的博客-程序员秘密

撰写时间:2018.3.8 内容概述:本博客主要介绍在ubuntu14.04的环境下如何安装afni。在官网上的该页面下介绍了好几种安装方式。本文采用的是1.2.3. To: download current precompiled AFNI binaries,使用预编译好的安装包,解压即能使用的这种。 系统环境:ubuntu14.041.下载预编译好的安装包ubuntu14.04...

sk-learn学习笔记三_from sklearn.feature_extraction.text import hashin_Mr_wuliboy的博客-程序员秘密

通过哈希技巧实现特征向量哈希技巧是无固定状态的,它把任意的数据块映射到固定数目的位置,并且保证相同的输入一定产生相同的输出,不同的输入尽可能产生不同的输出。scikitlearn提供了HashingVectorizer来实现这个技巧:from sklearn.feature_extraction.text import HashingVectorizercorpus = ['the', ...

随便推点

云计算市场风起云涌,产业未来发展前景如何?_新睿云.任义兵的博客-程序员秘密

三个相互依存、相互促进的技术趋势云计算、大数据、人工智能推动了第四次“工业革命”的数字化转型。尤其是云计算,作为这次“变革”的基础设施,它正在爆发出令人炫目的“光彩”。此外,根据近日国务院发展研究中心发布的《中国云计算产业发展白皮书》显示,2018年,中国云计算产业规模达到962.8亿元,较2017年增长39.2%,2019年产业规模预计超过千亿,达到1290.7亿元,到2021年,...

安卓不root访问data目录文件_Wihatow的博客-程序员秘密

一般情况下,我们调试程序的时候,需要查看程序的一些数据,我们会想到使用adb命令来完成,可能你会这样来访问:- 直接访问C:\Users\Wihatow\Desktop>adb [email protected]_A01:/ $ cd /[email protected]_A01:/data $ lsopendir failed, Permission denied255|[email protected]

Session的持久化_session持久化操作_caomiao2006的博客-程序员秘密

Session的持久化: Session的持久化就是将HttpSession对象从内存中转移到文件系统或数据库中,这样做的好处是:减少系统资源的占用,如果Servlet容器突然关闭或重启,或Web应用重启,这些持久化了的HttpSession对象可以再重新加载进来,对于客户端,还是使用同一个Session。 Session的持久化是由Session Manager来管理的,Tomc

libstdc++.so.6: version `GLIBCXX_3.4.15' not found_aiqing0119的博客-程序员秘密

./filezilla: /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (required by ./filezilla)如果是64位系统报错信息如下:./filezilla: /usr/lib64/libstdc++.so.6: version `GLIBCXX_3.4.15' not found (req

ubuntu /usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.15' not found_ubuntu file /usr/lib64/libstdc++.so.6 not found_nicekwell的博客-程序员秘密

I got this problem when I am installing chrome.出现该错误的原因是当前的GCC版本中,没有GLIBCXX_3.4.15,需要安装更高版本。我们可以输入:strings /usr/lib/libstdc++.so.6 | grep GLIBCXX,查看当前的GCC版本,结果如下:GLIBCXX_3.4 GLIBCXX_3.4.1 GLIB

Linux-GLIBCXX版本过低导致编译错误--version `GLIBCXX_3.4.20' not found_小银是猪的博客-程序员秘密

ubuntu系统下,运行python程序时出现如下问题:OSError: /home/chenyanyin/anaconda2/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.20' not found (required by /home/chenyanyin/anaconda2/lib/python2.7/site-packages/ale_

推荐文章

热门文章

相关标签