首先要截取上传动作,并用JS获取到要上传的文件资源,我们要用:
var fileobj =
document.getElementByTagName('input')[i].files[0];
来获取上传的文件资源,然后通过它这个对象里面继承的一个slice()方法来对二进制文件进行切割,每次切割的大小是在JS里设定的
这个过程里面关键问题有两点:
1、要把文件按顺序依次切割并依次发送到服务器
2、服务器接收到的是一块一块的二进制文件块,要把这些文件块按顺序组合到一起
对于第一点,我们的做法是,关闭ajax的异步上传,并用while()循环不断切割,在while中记录每次切割的起始和结束位置(位置是按字节算的,1MB要换算成1*1024*1024字节),然后依次send(),这里面要注意的问题有三点:
1、要在while前获取到文件总大小,这个通过fileobj.size可以得到。
2、ajax对象要每次循环时重新创建,否则当第一次send()完毕后,状态变为4,就发送完毕不会再发送后续文件了
3、被切割的数据依然要用FormData对象来append打包发送,否则服务器不认识这是文件数据
关键代码如下:
const SLICESIZE = 10 * 1024 * 1024;//每次切割的大小,这里是10MB
var start = 0;
var end = 0;
totalsize = fileobj.size;
while(start < totalsize){
start = end ;
end = start + SLICESIZE;
var xhr = new XMLHttpRequest();
var fd = new FormData();
var part
= fileobj.slice(start,end);//对文件进行切割,两个参数
fd.append('name',part);//把切割文件打包进FormData对象中以便发送,其中name的值就是服务器$_FILES['name']接受处的变量名
xhr.open('POST','test.php',false);//之所以关闭异步,是为了防止文件块送达服务器的顺序出错
xhr.send(fd);//把打包的数据发送
}
对于第二点,由于我们在ajax处已经关闭了异步,所以只需要把收到的文件块以append的方式追加到接受文件里即可,当所有文件块都追加完毕,则整个大文件就接受完毕了。
这个过程里关键问题有两点:
1、对于接受到的文件,我们要用file_get_contents()函数读取它的文件数据
2、读取完文件数据后,我们用file_put_contents()以APPEND追加的方式加入到保存的文件
这个地方保存的文件可能在第一次接收的时候不存在,所以需要用file_exists()判断一下,如果不存在的话,就把接收到的文件通过move_uploaded_file()把第一次传的文件移动到指定位置并命名,然后,后续再传上来的文件,就以APPEND追加进这个文件就OK了
关键代码:
//判断
file_put_contents('./upload/demo.mov',file_get_contents($_FILES['name']['tmp_name']),FILE_APPEND);
另外有一点值得补充:
由于JS是单线程的,加上浏览器的渲染原理,导致如果再ajax异步被关闭的情况下进行分块传送大文件,将造成两个明显的问题:
1、传送大文件所需的时间比较长。
2、ajax异步被关闭后,直到大文件全部传送完毕,后续程序,至少该函数才会结束。
那么这里有个问题就是关于动态改变CSS来处理进度条的问题,根据浏览器的渲染原理,如果JS某个函数一直处于执行状态,那么由此导致的一切可能造成浏览器repain的操作都得等到JS函数执行完毕后进行,期间,浏览器会把所有repain操作积蓄起来,等到JS执行完毕后,统一一次全部完成,所以这个地方的进度条设计要用到setInterval()来规定间隔多长时间触发一次计算和渲染才行