web-dev-qa-db-fra.com

Utilisation de FileReader.readAsArrayBuffer () sur les fichiers modifiés dans Firefox

Je rencontre un problème étrange en utilisant FileReader.readAsArrayBuffer qui ne semble affecter que Firefox (j'ai testé dans la version actuelle - v40). Je ne peux pas dire si je fais juste quelque chose de mal ou s'il s'agit d'un bogue Firefox.

J'ai du JavaScript qui utilise readAsArrayBuffer pour lire un fichier spécifié dans un <input> champ. Dans des circonstances normales, tout fonctionne correctement. Cependant, si l'utilisateur modifie le fichier après l'avoir sélectionné dans le <input> champ, readAsArrayBuffer peut devenir très confus.

ArrayBuffer je reviens de readAsArrayBuffer a toujours la longueur d'origine du fichier. Si l'utilisateur modifie le fichier pour l'agrandir, je n'obtiens aucun des octets après la taille d'origine. Si l'utilisateur modifie le fichier pour le rendre plus petit, le tampon est toujours de la même taille et le "surplus" dans le tampon est rempli avec les codes de caractères 90 (lettre majuscule "Z" si vue comme une chaîne).

Étant donné que ce code est si simple et fonctionne parfaitement dans tous les autres navigateurs que j'ai testés, je pense que c'est un problème avec Firefox. J'ai signalé comme bogue à Firefox mais je veux m'assurer que ce n'est pas seulement quelque chose d'évident que je fais mal.

Le comportement peut être reproduit par l'extrait de code suivant. Tout ce que tu dois faire est:

  1. Recherchez un fichier texte contenant 10 caractères (10 n'est pas un nombre magique - je ne l'utilise que comme exemple)
  2. Observez que le résultat est un tableau de 10 éléments représentant les codes de caractères de chaque élément
  3. Pendant qu'il est toujours en cours d'exécution, supprimez 5 caractères du fichier et enregistrez
  4. Observez que le résultat est toujours un tableau de 10 éléments - les 5 premiers sont corrects mais les 5 derniers sont tous 90 (lettre majuscule Z)
  5. Ajout de 10 caractères (le fichier contient donc 15 caractères)
  6. Notez que le résultat est toujours un tableau de 10 éléments - les 5 derniers ne sont pas retournés
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>

Dans le cas où l'extrait ne fonctionne pas, l'exemple de code est également disponible en tant que JSFiddle ici: https://jsfiddle.net/Lv5y9m2u/

18
Stephen McDaniel

Intéressant, on dirait que Firefox met en cache la taille du tampon même si le fichier est modifié.

Vous pouvez vous y référer link , remplacé readAsArrayBuffer avec sa fonctionnalité personnalisée qui utilise readAsBinaryString. Son fonctionne bien dans Firefox et Chrome

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();
11

Je pense que vous rencontrez un bug de Firefox. Cependant, comme vous l'avez souligné, readAsArrayBuffer se comporte correctement dans tous les navigateurs pris en charge, à l'exception de Firefox, tandis que readAsBinaryString est pris en charge par tous les navigateurs, sauf IE.

Par conséquent, il est possible de préférer readAsBinaryString lorsqu'il existe et de revenir à readAsArrayBuffer sinon.

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

Violon de travail: https://jsfiddle.net/Lv5y9m2u/6/

Prise en charge du navigateur:

  • Firefox: utilise readAsBinaryString, ce qui n'est pas problématique.
  • IE> = 10: utilise readAsArrayBuffer qui est pris en charge.
  • IE <= 9: l'intégralité de l'API FileReader n'est pas prise en charge.
  • Presque tous les autres navigateurs: utilise readAsBinaryString.
6
FelisCatus