2013-11-26 49 views
7

我想實現的是:片大文件到使用AJAX塊,上傳和HTML5的FileReader

在前端,我使用HTML5文件API來讀取該文件,然後上傳該文件的內容的php後端使用ajax,如果文件大小很小,也可以。但是,如果文件足夠大,則會導致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> 

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); 
      } 
    } 

的問題是,該文件的組塊之後都被上傳到服務器併合併成一個新的,總文件大小比原來小,並且合併的一個被破壞。問題在哪裏以及如何解決?

+0

也許找什麼塊是從最終的文件丟失?多次執行並查找出現的任何模式。 – Achshar

+0

http://kongaraju.blogspot.in/2012/07/large-file-upload-more-than-1gb-using.html試試這個,如果問題仍然存在 –

回答

0

此問題主要是由共享主機的全侷限制引起的。它們通常控制數據量,並在超出限制時丟棄連接。 我嘗試了幾次和幾次。始終在相同的位置上進行操作。目標文件較小且損壞。即使上傳的文件小於此大小,以便合併的數據達到極限,結果仍然可以。 YOu只有一個機會:獲取最大大小增加,文件被打開。 每當您打開目標文件並將塊內容寫入其中並且大小超過限制時,主機將斷開連接。示例主機:Strato 此處的限制全局設置爲16MB。我得到的最大合併結果大小是此大小的兩倍。沒有機會覆蓋它。

3

。在你的JS腳本 一個小錯誤,我注意到,reader.onprogress事件比XHR 負載事件觸發多次。在這種情況下,跳過了一些塊。嘗試增加加載函數中的加載的變量。

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

var reader = new FileReader(); 

reader.onload = function(e){ 
     var xhr = new XMLHttpRequest(); 
     var upload = xhr.upload; 
     upload.addEventListener('load',function(){ 
     loaded += step; 
     progress.value = (loaded/total) * 100; 
       if(loaded <= total){ 
         blob = file.slice(loaded,loaded+step); 

         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,step); 
reader.readAsBinaryString(blob); } 
3
  • 使用readAsBinaryString FN僅僅是不好的做法
  • SendAsBinary也depricated
  • 讀塊內容只是純粹達姆。切片就足夠了。
  • 切片也是不必要的,除非服務器不接受這樣的大文件(如dropbox限制REST API)
  • 所以如果有人試圖使用工作線程,base64或FileReader來上傳大文件 - 不要這樣做,這是沒有必要的。

只有那有什麼?讀/時間片的文件是,如果你決定要加密/解密/把它發送到服務器之前壓縮文件。
但僅限有限的時間,直到所有瀏覽器開始支持流。
那麼你應該看一看獲取和ReadableStream

fetch(url, {method: 'post', body: new ReadableStream({...})}) 

如果你只需要到BLOB轉發給服務器,只是做: xhr.send(blob_or_file)和瀏覽器將讀取照顧它(正確)並且不消耗任何內存。而該文件可以多麼大的file/blob是