Использование FileReader.readAsArrayBuffer() для измененных файлов в Firefox

Я сталкиваюсь с странной проблемой, используя FileReader.readAsArrayBufferэто только влияет на Firefox (я тестировал в текущей версии - v40). Я не могу сказать, делаю ли я что-то неправильно или это ошибка в Firefox.

У меня есть JavaScript, который используетreadAsArrayBufferчитать файл, указанный в<input>поле. В нормальных условиях все работает правильно. Однако, если пользователь изменяет файл после его выбора в <input> поле, readAsArrayBuffer может быть очень запутанным.

ArrayBufferЯ вернусь изreadAsArrayBufferвсегда имеет длину, в которой файл был изначально. Если пользователь изменяет файл, чтобы увеличить его, я не получаю ни одного байта после исходного размера. Если пользователь изменяет файл, чтобы он стал меньше, размер буфера остается прежним, а "избыток" в буфере заполняется кодами символов 90 (заглавная буква "Z", если рассматривать ее как строку).

Поскольку этот код очень прост и отлично работает в любом другом браузере, который я тестировал, я думаю, что это проблема Firefox. Я сообщил об этом как об ошибке в Firefox, но я хочу убедиться, что это не просто что-то очевидное, я делаю неправильно.

Поведение может быть воспроизведено с помощью следующего фрагмента кода. Все, что вам нужно сделать, это:

  1. Найдите текстовый файл, содержащий 10 символов (10 не волшебное число - я просто использую его в качестве примера)
  2. Обратите внимание, что в результате получается массив из 10 элементов, представляющих коды символов каждого элемента.
  3. Пока он еще работает, удалите 5 символов из файла и сохраните
  4. Обратите внимание, что результат по-прежнему массив из 10 элементов - первые 5 верны, но последние 5 все 90 (заглавная буква Z)
  5. Теперь добавлено 10 символов (поэтому файл теперь имеет длину 15 символов)
  6. Обратите внимание, что результат по-прежнему массив из 10 элементов - последние 5 не возвращаются

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>

Если фрагмент не работает, пример кода также доступен в виде JSFiddle здесь: https://jsfiddle.net/Lv5y9m2u/

2 ответа

Решение

Интересно, что FF кеширует размер буфера, даже если файл изменен.

Вы можете обратиться к этой ссылке, заменив readAsArrayBuffer на пользовательский функционал, который использует readAsBinaryString. работает нормально в FF и 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();

Я думаю, что вы попали в ошибку Firefox. Однако, как вы указали, readAsArrayBuffer ведет себя корректно во всех поддерживаемых браузерах, кроме Firefox readAsBinaryString поддерживается всеми браузерами, кроме IE.

Поэтому можно предпочесть readAsBinaryString когда он существует и возвращается к 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);
    }
}

Использование:

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

Рабочая скрипка: https://jsfiddle.net/Lv5y9m2u/6/

Поддержка браузера:

  • Firefox: использует readAsBinaryString, что не является проблематичным.
  • IE> = 10: использует readAsArrayBuffer который поддерживается.
  • IE <= 9: весь FileReader API не поддерживается.
  • Почти все другие браузеры: использует readAsBinaryString,
Другие вопросы по тегам