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

    正文概述 掘金(EvalStudio)   2021-08-18   487
    • 介绍常见的文件上传方式
    • 大文件上传的通病:容易超时。介绍文件分片断点续传方法。

    文件上传

    编码后上传

    图片转base64上传

    前端将需要上传的图片进行base64编码,然后提交到服务端。

    var imgURL = URL.createObjectURL(file);
    var canvas = document.getElementById('canvas');
    var ctx = canvas.getContext('2d');
    ctx.drawImage(imgURL, 0, 0);
    // 获取图片的编码,然后将图片当做是一个很长的字符串进行传递
    var data = canvas.toDataURL("image/jpeg", 0.5);
    

    服务端接收到文件后,进行base64解码,然后进行保存。

    一般只在图片比较小的时候建议用base64编码,原因是编码后文本体积会比原图片更大,它将三个字节转化成四个字节。因此对于体积较大的文件来说,上传和解析的时间会增加。

    读取文件转二进制格式上传

    前端直接读取文件内容后以二进制格式上传

    // 读取二进制文件
    function readBinary(text){
       var data = new ArrayBuffer(text.length);
       var ui8a = new Uint8Array(data, 0);
       for (var i = 0; i < text.length; i++){ 
         ui8a[i] = (text.charCodeAt(i) & 0xff);
       }
       console.log(ui8a)
    }
    
    var reader = new FileReader();
    reader.onload = function(){
    	  readBinary(this.result) // 读取result或直接上传
    }
    
    // 把从input里读取的文件内容,放到fileReader的result字段里
    reader.readAsBinaryString(file);
    

    form表单上传

    使用form标签,并指定标签的enctype="multipart/form-data",表明表单需要上传二进制数据,并设置method="POST",。

    <form action="http://localhost:8080" method="POST" enctype="multipart/form-data">
      <input type="file" name="file1">
      <input type="submit">
    </form>
    

    使用type=submit上传文件,体验上的缺点很明显,上传完毕会刷新页面,导致页面数据和状态丢失。 早期会把表单使用iframe内嵌到页面里,提交完只刷新iframe,实现局部刷新的效果。 ​

    通过xhr,前端也可以进行异步上传文件的操作,实现无刷新上传。 ​

    FormData上传

    使用FormData对象管理表单数据,再将表单数据进行异步提交。

    let files = e.target.files // 获取input的file对象
    let formData = new FormData(); // 构造FormData对象
    formData.append('file', file);
    axios.post(url, formData);
    

    大文件上传

    以上的传输方式在文件不大时运行良好,但是在文件很大的情况下,比如一个视频文件几百上千兆,就会出现问题: ​

    在同一个请求中,上传的数据量太大,容易导致链接超时失效,或是超过服务器可接收最大字段。而上传失败后整个文件需要重传。

    文件分片 解决大文件上传问题,关键技术就是将大的文件分割成一个个小的文件,并行上传。 ​

    还原分片 接收端在接收完所有分片文件后,将一个个小文件按照顺序拼接还原。 ​

    断点续传 在传输过程中意外中断时,我们并不希望将所有文件都进行重传,而是只把上传失败的部分进行重传。

    文件分片

    Blob对象:表示一个不可变、原始数据的类文件对象。它包含一个重要的方法slice(),通过这个方法,我们就可以对二进制文件进行拆分。 ​

    File对象:File对象是特殊类型的Blob,它继承blob接口的方法。 ​

    文件分片关键步骤:

    • 前端将大文件进行分片,分割成一个个小文件
    • 前端并行发送分片文件
    • 服务端接收分片文件
    • 分片文件传送完毕后,前端发送一个标志结束的请求
    • 服务端接收到结束标志后,把分片文件进行合并
    • 合并完成后,删除分片文件

    分片代码示例:

    function sliceInPieces(file, size = 2 * 1024 * 1024) {
      const totalSize = file.size; // 文件总大小
      const chunks = [];
      let start = 0;
      let end = 0;
    
      while (start < totalSize) {
        end = start + size;
        
        const blob = file.slice(start, end); // 调用对象上的slice方法
        chunks.push(blob);
    
        start = end;
      }
      return chunks;
    }
    

    上传分片代码示例:(如果分片比较多,并发请求的数量需要控制一下) ​

    const file =  document.querySelector("[name=file]").files[0]; // 读取文件
    
    const chunks = sliceInPieces(file); // 分片
    
    const context = uuid();  // 文件唯一标识
    
    const promiseList = [];
    
    chunks.forEach((chunk, index) => {
      let fd = new FormData();
      fd.append("file", chunk);
      fd.append("context", context); // 带上标识
      fd.append("index", index); // 带上位置序号
      promiseList.push(axios.post(url, fd)); 
    })
    
    
    Promise.all(promiseList).then((res) => {
      ...
      // 全部上传完毕后通知接收端结束
    	let fd = new FormData();
      fd.append("context", context);
      fd.append("chunks", chunks.length);
      axios.post(doneUrl, fd).then(res => {
        ...
      });
    })
    

    还原分片

    接收端处理分片需要注意的问题:

    • 如何识别分片文件来自同一个源文件?
    • 如何将多个分片还原成一个文件?

    区分来自同一个源文件 对源文件生成一个文件唯一标识context参数,标志文件分片来自同一个源文件。在每个切片请求上把context参数带上,通知结束的接口也带上这个标记值, 接收端根据这个标记值确认接收到的分片属于同一个文件。 ​

    这个context值是文件的唯一标识,下面例举几种生成方式:

    1. 用文件名等作为标识,但是为了避免不同用户取了相同的文件名,可以再拼接上用户信息,比如uid保证唯一性。
    2. 用md5生成文件hash作为文件的唯一标识。
    触发还原分片

    在所有分片上传完毕后,还会额外再发送一个请求通知接收端进行拼接。 接收端根据请求中的context值,找到所有带此context标志的分片,确认分片的顺序(可以通过分片请求中加的index参数,有些也会直接拼接在context后面,接收端进行处理),根据顺序合并分片文件。

    断点续传

    上面我们已经了解大文件上传的方法,大文件进行分片并上传,接收端再合并还原成大文件。但在等待分片上传的过程中,我们仍然很有可能发生一些意外的情况导致部分分片上传失败,如断网或者页面关闭/刷新等。由于分片没有全部上传成功,因此无法通知接收端进行文件还原。如果再次全部重新上传,已上传成功的分片就浪费了。因此我们可以通过断点续传来进行处理。

    断点续传:只对上传失败的分片进行重传。关键点是客户端如何感知哪些分片已上传成功。 前端触发重传时,根据以上传成功的分片进行筛选,只对未成功的分片进行上传。全部上传完毕后再进行合并的额外请求。

    那前端如何感知已上传的分片的信息呢?

    • 客户端记录,通过locaStorage等方式保存在客户端上。
      • 优点:方便实现,且不依赖服务端。缺点:存在客户端上不保险,用户清除缓存记录就会丢失
    • 服务端记录,服务端额外提供一个查询接口给前端调用。
      • 优点:由服务端根据已接受到的分片给客户端开一个已上传的记录接口,客户端重传前进行调用。记录不容易丢失。缺点:额外的接口开销。

    分片过期:在分片的步骤中,最后一步是合并完成后删除分片。如果客户端一直不调用通知上传完成的接口,这些分片就一直会保存在磁盘中,这显然是不合理的。因此,分片还需要带上有效期,超期需要被清理掉。在断点续传时,也同样需要考虑到过期的问题。

    最后

    微信搜索公众号Eval Studio,关注更多动态。


    下载网 » 大文件上传

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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