2015-08-25 12 views
15

Sto incontrando un problema strano utilizzando FileReader.readAsArrayBuffer che sembra interessare solo Firefox (ho provato nella versione corrente - v40). Non posso dire se sto facendo qualcosa di sbagliato o se si tratta di un bug di Firefox.Utilizzo di FileReader.readAsArrayBuffer() sui file modificati in Firefox

Ho qualche JavaScript che utilizza readAsArrayBuffer per leggere un file specificato in un campo <input>. In circostanze normali, tutto funziona correttamente. Tuttavia, se l'utente modifica il file dopo averlo selezionato nel campo <input>, readAsArrayBuffer può diventare molto confuso.

Il ArrayBuffer ritorno da readAsArrayBuffer ha sempre la lunghezza del file originale. Se l'utente cambia il file per ingrandirlo, non ottengo nessuno dei byte dopo la dimensione originale. Se l'utente cambia il file per renderlo più piccolo, il buffer ha ancora la stessa dimensione e l''eccesso' nel buffer è riempito con i codici di carattere 90 (maiuscola 'Z' se vista come una stringa).

Poiché questo codice è così semplice e funziona perfettamente in tutti gli altri browser che ho provato, penso che sia un problema di Firefox. Io ho reported it as a bug per Firefox ma voglio assicurarmi che questo non sia solo ovvio che sto sbagliando.

Il comportamento può essere riprodotto dal seguente frammento di codice. Tutto quello che dovete fare è:

  1. Cerca un file di testo che dispone di 10 caratteri in esso (10 non è un numero magico - sto solo usando come un esempio)
  2. Osservare che il risultato è una serie di 10 elementi che rappresentano i codici carattere di ogni articolo
  3. Mentre è ancora in esecuzione, eliminare 5 caratteri dal file e salvare
  4. Osservare che il risultato è ancora una matrice di 10 elementi: i primi 5 sono corretti ma gli ultimi 5 sono tutti 90 (maiuscola Z)
  5. Ora aggiunto 10 caratteri (quindi il file è ora 15 chara cters lunghi)
  6. Osservare che il risultato è ancora un array di 10 elementi - l'ultimo 5 non vengono restituiti

function ReadFile() { 
 
    var input = document.getElementsByTagName("input")[0]; 
 
    var output = document.getElementsByTagName("textarea")[0]; 
 

 
    if (input.files.length === 0) { 
 
    output.value = 'No file selected'; 
 
    window.setTimeout(ReadFile, 1000); 
 
    return; 
 
    } 
 

 
    var fr = new FileReader(); 
 
    fr.onload = function() { 
 
    var data = fr.result; 
 
    var array = new Int8Array(data); 
 
    output.value = JSON.stringify(array, null, ' '); 
 
    window.setTimeout(ReadFile, 1000); 
 
    }; 
 
    fr.readAsArrayBuffer(input.files[0]); 
 

 
    //These two methods work correctly 
 
    //fr.readAsText(input.files[0]); 
 
    //fr.readAsBinaryString(input.files[0]); 
 
} 
 

 
ReadFile();
<input type="file" /> 
 
<br/> 
 
<textarea cols="80" rows="10"></textarea>

Nel caso frammento non funziona, il codice di esempio è anche disponibile come JSFiddle qui: https://jsfiddle.net/Lv5y9m2u/

+0

Firefox potrebbe avere problemi con quello infatti ... Il tentativo di caricare il violino si è schiantato il mio Nightly. Ora funziona, però. – Oriol

+0

@Oriol Come parte della sperimentazione di questa piccola versione e della mia app più grande che utilizza questa logica, mi sono imbattuto in molti arresti anomali in Firefox. :-(Io non uso Firefox (eccetto per testare la compatibilità cross-browser) quindi non ero sicuro se Firefox fosse solo buggato in generale, buggato con FileReader o cosa. _Nessun attacco destinato ai fan di Firefox_ –

+0

Forse correlato al fatto che FF non aggiorna i 'file' dell'input se il nome è lo stesso (nessun evento onchange attivato) – Kaiido

risposta

7

Interessante, sembra che FF memorizzi nella cache la dimensione del buffer anche se il file è stato modificato.

è possibile fare riferimento a questo link, sostituito readAsArrayBuffer con è una funzionalità personalizzata che utilizza readAsBinaryString. funziona bene in FF e cromato

function ReadFile() { 
var input = document.getElementsByTagName("input")[0]; 
var output = document.getElementsByTagName("textarea")[0]; 

if (input.files.length === 0) { 
    output.value = 'No file selected'; 
    window.setTimeout(ReadFile, 1000); 
    return; 
} 

var fr = new FileReader(); 
fr.onload = function() { 
    var data = fr.result; 
    var array = new Int8Array(data); 
    output.value = JSON.stringify(array, null, ' '); 
    window.setTimeout(ReadFile, 1000); 
}; 
fr.readAsArrayBuffer(input.files[0]); 



//These two methods work correctly 
//fr.readAsText(input.files[0]); 
//fr.readAsBinaryString(input.files[0]); 
} 
if (FileReader.prototype.readAsArrayBuffer && FileReader.prototype.readAsBinaryString) { 
    FileReader.prototype.readAsArrayBuffer = function readAsArrayBuffer() { 
     this.readAsBinaryString.apply(this, arguments); 
     this.__defineGetter__('resultString', this.__lookupGetter__('result')); 
     this.__defineGetter__('result', function() { 
      var string = this.resultString; 
      var result = new Uint8Array(string.length); 
      for (var i = 0; i < string.length; i++) { 
       result[i] = string.charCodeAt(i); 
      } 
      return result.buffer; 
     }); 
    }; 
} 
ReadFile(); 
+1

Sfortunatamente, readAsBinaryString non è supportato nemmeno nell'ultima versione di Internet Explorer. Vedere: http://caniuse.com/#search=filereader. Forse potrei applicare la soluzione alternativa solo quando readAsBinaryString esiste (dopo tutto, readAsArrayBuffer sembra funzionare bene in IE) –

+2

Ho aggiornato questa risposta per includere un controllo sull'esistenza di 'readAsBinaryString'. Ciò consente a questo codice di funzionare in IE. Ora soddisfa tutto ciò di cui avevo bisogno - funziona s su tutti i browser di cui ho bisogno ed è facile passare alla mia base di codice esistente senza modificare nulla. Sarà anche facile da rimuovere in futuro se/quando verrà corretto il bug di Firefox. –

3

Penso che tu stia colpendo un baco di Firefox. Tuttavia, come hai sottolineato, readAsArrayBuffer si comporta correttamente in tutti i browser supportati tranne Firefox mentre readAsBinaryString è supportato da tutti i browser tranne IE.

Pertanto, è possibile preferire readAsBinaryString quando esiste e, in caso contrario, tornare a readAsArrayBuffer.

function readFileAsArrayBuffer(file, success, error) { 
    var fr = new FileReader(); 
    fr.addEventListener('error', error, false); 
    if (fr.readAsBinaryString) { 
     fr.addEventListener('load', function() { 
      var string = this.resultString != null ? this.resultString : this.result; 
      var result = new Uint8Array(string.length); 
      for (var i = 0; i < string.length; i++) { 
       result[i] = string.charCodeAt(i); 
      } 
      success(result.buffer); 
     }, false); 
     return fr.readAsBinaryString(file); 
    } else { 
     fr.addEventListener('load', function() { 
      success(this.result); 
     }, false); 
     return fr.readAsArrayBuffer(file); 
    } 
} 

Usage:

readFileAsArrayBuffer(input.files[0], function(data) { 
    var array = new Int8Array(data); 
    output.value = JSON.stringify(array, null, ' '); 
    window.setTimeout(ReadFile, 1000); 
}, function (e) { 
    console.error(e); 
}); 

violino di lavoro: Supporto https://jsfiddle.net/Lv5y9m2u/6/

Browser:

  • Firefox: Usa readAsBinaryString, che non è problematico.
  • IE> = 10: utilizza readAsArrayBuffer che è supportato.
  • IE < = 9: l'intera API FileReader non è supportata.
  • Quasi tutti gli altri browser: utilizza readAsBinaryString.
+1

Grazie, la soluzione è buona. Ho accettato [Pavan's] (http://stackoverflow.com/a/32343734/385996) perché quel codice è facile da inserire in un'app esistente senza dover modificare nulla. Poiché questo è (si spera) un bug temporaneo per risolvere il problema, avere una semplice soluzione drop-in è proprio quello di cui ho bisogno. –

Problemi correlati