将大文件切成块并使用ajax和html5 FileReader上传

use*_*467 11 javascript php html5 drag-and-drop filereader

我想要实现的是:

在前端,我使用html5文件api来读取文件,然后使用ajax将文件的内容上传到php后端,如果文件大小很小就可以了.但是,如果文件足够大,则会导致chrome崩溃.所以我使用file.slice将大文件拆分成块,当所有块都上传到php时,将块合并为一个完整的块.

代码如下:

前端:

<style>
#container {
     min-width:300px;
     min-height:200px;
     border:3px dashed #000;
}
</style>
<div id='container'>

</div>
<script>
function addDNDListener(obj){
    obj.addEventListener('dragover',function(e){
            e.preventDefault();
            e.stopPropagation();
    },false);
    obj.addEventListener('dragenter',function(e){
            e.preventDefault();
            e.stopPropagation();
    },false);
    obj.addEventListener('drop',function(e){
            e.preventDefault();
            e.stopPropagation();
            var ul = document.createElement("ul");
            var filelist = e.dataTransfer.files;
            for(var i=0;i<filelist.length;i++){
                    var file = filelist[i];
                    var li = document.createElement('li');
                    li.innerHTML = '<label id="'+file.name+'">'+file.name+':</label>  <progress value="0" max="100"></progress>';
                    ul.appendChild(li);
            }
            document.getElementById('container').appendChild(ul);
            for(var i=0;i<filelist.length;i++){
                    var file = filelist[i];
                    uploadFile(file);
            }
    },false);
}

function uploadFile(file){
    var loaded = 0;
    var step = 1024*1024;
    var total = file.size;
    var start = 0;
    var progress = document.getElementById(file.name).nextSibling;

    var reader = new FileReader();

    reader.onprogress = function(e){
            loaded += e.loaded;
            progress.value = (loaded/total) * 100;
    };

    reader.onload = function(e){
            var xhr = new XMLHttpRequest();
            var upload = xhr.upload;
            upload.addEventListener('load',function(){
                    if(loaded <= total){
                            blob = file.slice(loaded,loaded+step+1);
                            reader.readAsBinaryString(blob);
                    }else{
                            loaded = total;
                    }
            },false);
            xhr.open("POST", "upload.php?fileName="+file.name+"&nocache="+new Date().getTime());
            xhr.overrideMimeType("application/octet-stream");
            xhr.sendAsBinary(e.target.result);
    };
    var blob = file.slice(start,start+step+1);
    reader.readAsBinaryString(blob);
}

window.onload = function(){

    addDNDListener(document.getElementById('container'));
    if(!XMLHttpRequest.prototype.sendAsBinary){ 
              XMLHttpRequest.prototype.sendAsBinary = function(datastr) {  
                        function byteValue(x) {  
                            return x.charCodeAt(0) & 0xff;  
                        }  
                        var ords = Array.prototype.map.call(datastr, byteValue);  
                        var ui8a = new Uint8Array(ords);  
                        try{
                            this.send(ui8a);
                        }catch(e){
                            this.send(ui8a.buffer);
                        }  
              };  
    }
};
</script>
Run Code Online (Sandbox Code Playgroud)

php代码:

<?php
     $filename = "upload/".$_GET['fileName'];
     //$filename = "upload/".$_GET['fileName']."_".$_GET['nocache'];
     $xmlstr = $GLOBALS['HTTP_RAW_POST_DATA'];
     if(empty($xmlstr)){
             $xmlstr = file_get_contents('php://input');
     }
     $is_ok = false;
     while(!$is_ok){
            $file = fopen($filename,"ab");

            if(flock($file,LOCK_EX)){
                    fwrite($file,$xmlstr);
                    flock($file,LOCK_UN);
                    fclose($file);
                    $is_ok = true;
            }else{
                    fclose($file);
                    sleep(3);
            }
    }
Run Code Online (Sandbox Code Playgroud)

问题是,在文件块全部上传到服务器并合并为新文件后,总文件大小小于原始文件大小,合并后的文件大小被破坏.问题在哪里以及如何解决?

End*_*ess 5

  • 使用readAsBinaryString fn只是一个坏习惯
  • 还描述了SendAsBinary
  • 读取块内容只是纯粹的傻瓜。切片就足够了。xhr.send(blob.slice(0,10))
  • 除非服务器不接受如此大的文件(例如,受保管箱限制的REST API),否则切片也是不必要的
  • 因此,如果有人想聪明地使用工作线程,base64或FileReader来上传大文件-不用这样做,那就没有必要了。

只有当您决定在将文件发送到服务器之前对其进行加密/解密/压缩时,才可以读取/切片该文件。
但仅在有限的时间内,直到所有浏览器开始支持流。
然后,您应该看看fetch和ReadableStream

fetch(url, {method: 'post', body: new ReadableStream({...})})
Run Code Online (Sandbox Code Playgroud)

如果您只需要将Blob转发到服务器,请执行以下操作: xhr.send(blob_or_file)浏览器将负责(正确)读取它,而不消耗任何内存。而且文件可以很大,但文件/斑点是

  • 这不是“哑巴”先生。看,互联网连接不好的人很难将大文件上传回服务器。这种方法允许他们在中断后恢复上传。 (3认同)

小智 3

这个问题主要是由于共享主机的全局限制造成的。它们通常控制数据量,并在限制被覆盖时丢弃连接。我尝试了好几次,也尝试了几种方法。总是停留在同一个位置。目标文件较小且已损坏。即使上传比这个大小更小的文件,使合并后的数据符合限制,结果也可以。您只有一次机会:增加要打开的文件的最大大小。每次打开目标文件并将块内容写入其中,并且大小覆盖限制时,托管者都会中断连接。示例主机:Strato 这里的限制全局设置为 16MB。我得到的最大合并结果大小是这个大小的两倍。没有机会覆盖它。