JavaScript上传大文件并支持中途取消上传
最近遇见一个需要上传超大大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现。
在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表格数据、上传影音文件等。如果文件体积比较大,或者网络条件不好时,上传的时间会比较长(要传输更多的报文,丢包重传的概率也更大),用户不能刷新页面,只能耐心等待请求完成。
下面从文件上传方式入手,整理大文件上传的思路,并给出了相关实例代码,由于PHP内置了比较方便的文件拆分和拼接方法,因此服务端代码使用PHP进行示例编写。
本文相关示例代码位于github上,主要参考
聊聊大文件上传
大文件切割上传
文件上传的几种方式
首先我们来看看文件上传的几种方式。
普通表单上传
使用PHP来展示常规的表单上传是一个不错的选择。首先构建文件上传的表单,并指定表单的提交内容类型为enctype="multipart/form-data",表明表单需要上传二进制数据。
然后编写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/
欢迎入群一起讨论:374992201
JavaScript上传大文件并支持中途取消上传相关推荐
- SpringBoot上传大文件并支持中途取消上传
上周遇到这样一个问题,客户上传高清视频(1G以上)的时候上传失败. 一开始以为是session过期或者文件大小受系统限制,导致的错误. 查看了系统的配置文件没有看到文件大小限制, web.xml中se ...
- python上传大文件s3_使用Python boto3上传Windows EC2实例中的文件至S3存储桶中
一.创建终端节点 为什么要创建终端节点,把VPC和S3管理起来呢?如果不将VPC和S3通过终端节点管理起来,那么VPC中EC2实例访问S3存储桶是通过公共网络的:一旦关联起来,那么VPC中EC2实例访 ...
- php 上传大文件400,PHP fastcgi模式上传大文件(大约有300多K)报错
最近在项目中中上传图片时,大约有300多K,结果报了个服务器错误,以前从未遇到过,错误的内容如下: mod_fcgid: HTTP request length 132296 (so far) exc ...
- 华为云服务器 大文件,云服务器上传大文件
云服务器上传大文件 内容精选 换一换 本节操作介绍本地MacOS系统主机通过安装"Microsoft Remote Desktop for Mac"工具向Windows云服务器传输 ...
- [转载自简书] ASPNetCore上传大文件碰到的一些问题总结
上传大文件碰到不少问题,总结一下. 先介绍一下功能:ajax上传文件到NetCore的web api接口, 安装在Linux服务器的服务端通过Nginx接受到后再存到文件服务器上. 如果文件不大,这个 ...
- 使用LFS解决GitHub无法上传大文件问题
使用LFS解决GitHub无法上传大文件问题 今天使用GitHub上传几个比较大的pdf电子书,有的大小超过100MB了,结果GitHub报错提示无法上传大于100MB的文件,报错信息如下: remo ...
- php webuploader大文件,web uploader 上传大文件总结
由于业务需要,需要上传大文件,已有的版本无法处理IE版本,经过调研,百度的 webuploader 支持 IE 浏览器,而且支持计算MD5值,进而可以实现秒传的功能. 大文件上传主要分为三部分,预上传 ...
- [开源应用]利用HTTPHandler+resumableJs+HTML5实现拖拽上传[大]文件
前言: 大文件传输一直是技术上的一大难点.文件过大时,一些性提交所有的内容进内存是不现实的.大文件带来问题还有是否支持断点传输和多文件同时传输. 本文以resumableJs为例,介绍了如何在ASP. ...
- Asp.Net 上传大文件专题(4)--利用ajax技术显示上传进度
====================================================== 注:本文源代码点此下载 ================================= ...
最新文章
- 内存写越界导致破环堆结构引起的崩溃问题定位经验[如报错malloc(): memory corruption或free(): invalid next size]...
- python类型转换-Python中如何进行数据类型转换?
- Docker容器相关命令
- 人生的84000种可能
- apache的rewrite规则无法加载问题
- P3649-[APIO2014]回文串【PAM】
- (转)PowerDesigner教程系列(二)概念数据模型
- Flutter高级第4篇:inappbrowser、StatefulBuilder 更新 Flutter showDialog、showModalBottomSheet中的状态
- VIIRS SDR数据预处理(一)
- 基础连接已关闭解决办法_解决|罗技蓝牙键盘连接ipad后打不出字?
- qqkey获取原理_QQKEY获取多功能软件+【附源码】
- 欧式二元期权的定价公式及实现
- 内容过于真实,可能会引起不适-----程序员去面试的梗!面试官:“哦了,明天来上班吧”
- 哈啰出行高质量故障复盘法:“3+5+3”(附模板)
- 实体、协议、服务和服务访问点的概念
- [高项]关键路径法VS关键链法
- linux添加失败的原因,linux下execvp失败设置errno=7的原因是什么?
- Linux中误删文件的恢复方法
- 一位经贸学姐从点工到测试主管的心路历程(一)
- 【正点原子STM32连载】第二十一章 通用定时器实验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1