html5大文件上传技术
引言

普通html表单在上传几KB或几MB级别的文件基本无压力,但在上传上百MB甚至是GB级别大文件很无爱。而且一般服务器单个文件上传限制基本都在几MB之内。如何有效突破限制,上传大文件技术值得深入探讨。

html5大文件上传技术
一般解决方案
html5文件分割上传解决方案
代码实现
运行截图
上传文件相关问题讨论
安全问题
用户体验优化
上传性能优化
一般解决方案
html5文件分割上传
上传相关问题讨论
一般解决方案
一般解决方案粗劣总结主要由以下几种:

FTP

使用ftp一般能满足上传大文件需求,但是从用户角度来讲,ftp方式大众普及度不高,用户使用还得学习。 
理想的状态下是没必要让用户知道的就不需要让用户知道,无知是福,简单是美。不要去轻易挑战用户积累的使用习惯,让用户感觉不到上传大文件和小文件的差异。

相关组件插件开发

一些诸如网盘、邮箱之类的在上传大文件时会开发一些相关插件来解决问题,但从用户角度讲,网页顶部会弹出一些安装插件的提示,容易产生厌烦、抵触、怀疑等不良使用情绪,效果还是有些折扣。

修改服务器配置

也有一些修改服务器最大上传配置的做法,诸如修改php.ini、IIS之类,这种做法在用户界面层面是感觉不到差异了,但是这种做法相对粗糙,对资源利用的效率不高,大文件传输卡顿超时、浏览器崩溃等问题非常明显,使用体验还是很糟糕。

html5文件分割上传解决方案
html5提供的文件API中可以轻松的对文件进行分割切片,然后通过javascript异步处理向服务器传输数据,突破对大文件上传的限制,同时异步处理在一定程度上也提高了文件上传的效率。用户体验上也优于前述方案。

代码实现
下面以php为例,讲一下具体实现过程。在html端将文件进行切割,提交给upload.php1,upload.php将上传的文件片移到文件夹临时存放.当最后一块文件片成功提交到upload.php后,向merge.php提交一个合并文件片的请求。2

核心代码如下: 

index.html
<!DOCTYPE html>
 <html>
 <head lang="en">
     <meta charset="UTF-8">
     <title>大文件上传实例</title>
     <script type="text/javascript">
         const BYTES_PER_CHUNK = 1024 * 1024; // 每个文件切片大小定为1MB .
         var slices;
         var totalSlices;        //发送请求
         function sendRequest() {            var blob = document.getElementById('file').files[0];
            var start = 0;
             var end;
             var index = 0;            // 计算文件切片总数
             slices = Math.ceil(blob.size / BYTES_PER_CHUNK);
             totalSlices= slices;            while(start < blob.size) {
                 end = start + BYTES_PER_CHUNK;
                 if(end > blob.size) {
                     end = blob.size;
                 }                uploadFile(blob, index, start, end);
                start = end;
                 index++;
             }
         }        //上传文件
         function uploadFile(blob, index, start, end) {
             var xhr;
             var fd;
             var chunk;            xhr = new XMLHttpRequest();
             xhr.onreadystatechange = function() {
                 if(xhr.readyState == 4) {
                     if(xhr.responseText) {
                         alert(xhr.responseText);
                     }                    slices--;
                    // 如果所有文件切片都成功发送,发送文件合并请求。
                     if(slices == 0) {
                         mergeFile(blob);
                         alert('文件上传完毕');
                     }
                 }
             };             chunk =blob.slice(start,end);//切割文件
            //构造form数据
             fd = new FormData();
             fd.append("file", chunk);
             fd.append("name", blob.name);
             fd.append("index", index);            xhr.open("POST", "upload.php", true);
            //设置二进制文边界件头
             xhr.setRequestHeader("X_Requested_With", location.href.split("/")[3].replace(/[^a-z]+/g, '$'));
             xhr.send(fd);
         }        function mergeFile(blob) {
             var xhr;
             var fd;            xhr = new XMLHttpRequest();
            fd = new FormData();
             fd.append("name", blob.name);
             fd.append("index", totalSlices);            xhr.open("POST", "merge.php", true);
             xhr.setRequestHeader("X_Requested_With", location.href.split("/")[3].replace(/[^a-z]+/g, '$'));
             xhr.send(fd);
         }    </script>
 </head>
 <body><input type="file" id="file"/>
 <button  οnclick="sendRequest()">上传</button>
 </body>
 </html>