vue大文件上传解决方案(500M以上)

最近碰见一个须要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,所以在此整理前端大文件上传相关功能的实现。php

在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据、上传影音文件等。若是文件体积比较大,或者网络条件很差时,上传的时间会比较长(要传输更多的报文,丢包重传的几率也更大),用户不能刷新页面,只能耐心等待请求完成。前端

下面从文件上传方式入手,整理大文件上传的思路,并给出了相关实例代码,因为PHP内置了比较方便的文件拆分和拼接方法,所以服务端代码使用PHP进行示例编写。java

本文相关示例代码位于github上,主要参考ios

聊聊大文件上传git

大文件切割上传github

文件上传的几种方式web

首先咱们来看看文件上传的几种方式。数据库

普通表单上传json

使用PHP来展现常规的表单上传是一个不错的选择。首先构建文件上传的表单,并指定表单的提交内容类型为enctype="multipart/form-data",代表表单须要上传二进制数据。canvas

而后编写index.php上传文件接收代码,使用move_uploaded_file方法便可(php大法好…)

form表单上传大文件时,很容易碰见服务器超时的问题。经过xhr,前端也能够进行异步上传文件的操做,通常由两个思路。

文件编码上传

第一个思路是将文件进行编码,而后在服务端进行解码,以前写过一篇在前端实现图片压缩上传的博客,其主要实现原理就是将图片转换成base64进行传递

varimgURL = URL.createObjectURL(file);

ctx.drawImage(imgURL, 0, 0);

// 获取图片的编码,而后将图片当作是一个很长的字符串进行传递

vardata= canvas.toDataURL( "image/jpeg", 0.5);

在服务端须要作的事情也比较简单,首先解码base64,而后保存图片便可

$imgData = $_REQUEST[ 'imgData'];

$base64 = explode( ',', $imgData)[ 1];

$img = base64_decode($base64);

$url = './test.jpg';

if(file_put_contents($url, $img)) {

exit(json_encode( array(

url => $url

)));

}

base64编码的缺点在于其体积比原图片更大(由于Base64将三个字节转化成四个字节,所以编码后的文本,会比原文本大出三分之一左右),对于体积很大的文件来讲,上传和解析的时间会明显增长。

更多关于base64的知识,能够参考Base64笔记。

除了进行base64编码,还能够在前端直接读取文件内容后以二进制格式上传

// 读取二进制文件

functionreadBinary(text){

vardata = newArrayBuffer(text.length);

varui8a = newUint8Array(data, 0);

for( vari = 0; i < text.length; i++){

ui8a[i] = (text.charCodeAt(i) & 0xff);

}

console.log(ui8a)

}

varreader = newFileReader;

reader. = function{

readBinary( this.result) // 读取result或直接上传

}

// 把从input里读取的文件内容,放到fileReader的result字段里

reader.readAsBinaryString(file);

formData异步上传

FormData对象主要用来组装一组用 发送请求的键/值对,能够更加灵活地发送Ajax请求。可使用FormData来模拟表单提交。

letfiles = e.target.files // 获取input的file对象

letformData = newFormData;

formData.append( 'file', file);

axios.post(url, formData);

服务端处理方式与直接form表单请求基本相同。

iframe无刷新页面

在低版本的浏览器(如IE)上,xhr是不支持直接上传formdata的,所以只能用form来上传文件,而form提交自己会进行页面跳转,这是由于form表单的target属性致使的,其取值有

_self,默认值,在相同的窗口中打开响应页面

_blank,在新窗口打开

_parent,在父窗口打开

_top,在最顶层的窗口打开

framename,在指定名字的iframe中打开

若是须要让用户体验异步上传文件的感受,能够经过framename指定iframe来实现。把form的target属性设置为一个看不见的iframe,那么返回的数据就会被这个iframe接受,所以只有该iframe会被刷新,至于返回结果,也能够经过解析这个iframe内的文原本获取。

functionupload{

varnow = + newDate

varid = 'frame'+ now

$( "body").append( `<iframe style="display:none;" name="${id}" id="${id}" />`);

var$form = $( "#myForm")

$form.attr({

"action": '/index.php',

"method": "post",

"enctype": "multipart/form-data",

"encoding": "multipart/form-data",

"target": id

}).submit

$( "#"+id).on( "load", function{

varcontent = $( this).contents.find( "body").text

try{

vardata = JSON.parse(content)

} catch(e){

console.log(e)

}

})

}

大文件上传

如今来看看在上面提到的几种上传方式中实现大文件上传会碰见的超时问题,

表单上传和iframe无刷新页面上传,实际上都是经过form标签进行上传文件,这种方式将整个请求彻底交给浏览器处理,当上传大文件时,可能会碰见请求超时的情形

经过fromData,其实际也是在xhr中封装一组请求参数,用来模拟表单请求,没法避免大文件上传超时的问题

编码上传,咱们能够比较灵活地控制上传的内容

大文件上传最主要的问题就在于:在同一个请求中,要上传大量的数据,致使整个过程会比较漫长,且失败后须要重头开始上传。试想,若是咱们将这个请求拆分红多个请求,每一个请求的时间就会缩短,且若是某个请求失败,只须要从新发送这一次请求便可,无需从头开始,这样是否能够解决大文件上传的问题呢?

综合上面的问题,看来大文件上传须要实现下面几个需求

支持拆分上传请求(即切片)

支持断点续传

支持显示上传进度和暂停上传

接下来让咱们依次实现这些功能,看起来最主要的功能应该就是切片了。

文件切片

参考: 大文件切割上传

编码方式上传中,在前端咱们只要先获取文件的二进制内容,而后对其内容进行拆分,最后将每一个切片上传到服务端便可。

在Java中,文件FIle对象是Blob对象的子类,Blob对象包含一个重要的方法slice,经过这个方法,咱们就能够对二进制文件进行拆分。

下面是一个拆分文件的示例,对于up6来讲开发者不须要关心拆分的细节,由控件帮助实现,开发者只须要关心业务逻辑便可。

控件上传的时候会为每个文件块数据添加相关的信息,开发者在服务端接收到数据后能够自已进行处理。

服务器接收到这些切片后,再将他们拼接起来就能够了,下面是PHP拼接切片的示例代码

对于up6来讲,开发人员不须要进行拼接,up6已经提供了示例代码,已经实现了这个逻辑。

保证惟一性,控件会为每个文件块添加信息,如块索引,块MD5,文件MD5

断点续传

up6自带续传功能,up6在服务端已经保存了文件的信息,在客户端也保存了文件的进度信息。在上传时控件会自动加载文件进度信息,开发者不须要关心这些细节。在文件块的处理逻辑中只须要根据文件块索引来识别便可。

此时上传时刷新页面或者关闭浏览器,再次上传相同文件时,以前已经上传成功的切片就不会再从新上传了。

服务端实现断点续传的逻辑基本类似,只要在getUploadSliceRecord内部调用服务端的查询接口获取已上传切片的记录便可,所以这里再也不展开。

此外断点续传还须要考虑切片过时的状况:若是调用了mkfile接口,则磁盘上的切片内容就能够清除掉了,若是客户端一直不调用mkfile的接口,听任这些切片一直保存在磁盘显然是不可靠的,通常状况下,切片上传都有一段时间的有效期,超过该有效期,就会被清除掉。基于上述缘由,断点续传也必须同步切片过时的实现逻辑。

续传效果

 

上传进度和暂停

经过xhr.upload中的progress方法能够实现监控每个切片上传进度。

上传暂停的实现也比较简单,经过xhr.abort能够取消当前未完成上传切片的上传,实现上传暂停的效果,恢复上传就跟断点续传相似,先获取已上传的切片列表,而后从新发送未上传的切片。

因为篇幅关系,上传进度和暂停的功能这里就先不实现了。

实现效果:

 

小结

目前社区已经存在一些成熟的大文件上传解决方案,如七牛SDK,腾讯云SDK等,也许并不须要咱们手动去实现一个简陋的大文件上传库,可是了解其原理仍是十分有必要的。

本文首先整理了前端文件上传的几种方式,而后讨论了大文件上传的几种场景,以及大文件上传须要实现的几个功能

经过Blob对象的slice方法将文件拆分红切片

整理了服务端还原文件所需条件和参数,演示了PHP将切片还原成文件

经过保存已上传切片的记录来实现断点续传

还留下了一些问题,如:合并文件时避免内存溢出、切片失效策略、上传进度暂停等功能,并无去深刻或一一实现,继续学习吧

后端代码逻辑大部分是相同的,目前可以支持MySQL,Oracle,SQL。在使用前须要配置一下数据库,能够参考我写的这篇文章:http://blog.ncmem.com/wordpress/2019/08/12/java-http%E5%A4%A7%E6%96%87%E4%BB%B6%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0%E4%B8%8A%E4%BC%A0/

相关文章
相关标签/搜索