2013-11-26 14 views
7

Quello che voglio implementare è:fetta file di grandi dimensioni in pezzi e caricati utilizzando AJAX e HTML5 FileReader

Nel front-end, io uso il file HTML5 API per leggere il file, e quindi caricare il contenuto del file al backend php usando ajax, ed è ok se il filesize è piccolo. Tuttavia, se il file è abbastanza grande, causa il crash di Chrome. Quindi ho diviso il file grande in blocchi usando file.slice, quando tutti i blocchi sono stati caricati sul PHP, uniamo i blocchi in un singolo completo.

il codice è il seguente:

il front-end:

<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> 

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

Il problema è che, dopo che i pezzi del file tutto caricato a il server e fuso in uno nuovo, la dimensione totale del file è inferiore all'originale e quella unita è rotta. Dov'è il problema e come risolverlo?

+0

forse cercare quali pezzi mancano dal file finale? Fatelo più volte e cercate tutti gli schemi che emergono. – Achshar

+0

http://kongaraju.blogspot.in/2012/07/large-file-upload-more-than-1gb-using.html prova questo se il problema esiste ancora –

risposta

0

Questo problema è causato principalmente dalle restrizioni globali degli host condivisi. Controllano spesso la quantità di dati e rilasciano Connection se Limite è sovrascritto. Ho provato questo diverse volte e in diversi modi. Sempre stucking nella stessa posizione. il file di destinazione era più piccolo e danneggiato. Anche prendendo un file più piccolo per il caricamento di questa dimensione, in modo che i dati uniti si adattino al limite, il risultato è stato OK. Si può avere solo una possibilità: aumentare la dimensione massima, per i file da aprire. Ogni volta che apri il file di destinazione e scrivi il contenuto del chunk e le dimensioni sostituiscono il limite, l'hoster interromperà Connection. Esempio di hoster: Strato Qui il limite è impostato globalmente a 16 MB. Ottengo la dimensione massima del risultato dell'unione doppia di questa dimensione. Nessuna possibilità di annullarlo.

3

C'è un piccolo errore nello script js ho notato che il reader.onprogress evento viene attivato più volte rispetto alla XHR carico evento. In questo caso, alcuni blocchi vengono saltati. Prova ad incrementare la variabile caricata all'interno della funzione carica.

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
  • Uso del tasto Fn readAsBinaryString è solo cattiva pratica
  • SendAsBinary è anche depricated
  • Leggendo il contenuto pezzi è solo pura dum. Affettare loro è abbastanza.
  • Presse è anche inutile a meno che il server non accetta tali file di grandi dimensioni (come Dropbox limitata API REST) ​​
  • Quindi, se qualcuno cerca di essere intelligente su come utilizzare i thread di lavoro, base64 o FileReader per il caricamento di file di grandi dimensioni - don' Lo faccio, è tutto inutile.

Solo il tempo necessario per leggere/tagliare il file è se si decide di crittografare/decodificare/comprimere i file prima di inviarli al server.
Ma solo per un periodo di tempo limitato finché tutti i browser non iniziano a supportare i flussi.
allora dovreste dare un'occhiata a prendere e ReadableStream

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

se avete solo bisogno di trasmettere il blob al server, basta fare: xhr.send(blob_or_file) e il browser si prenderà cura di leggerlo (correttamente) e non consumare alcun ricordo. E il file può essere comunque grande il file/blob è