2015-08-25 4 views
15

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

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

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

Поскольку этот код настолько прост и отлично работает в каждом другом браузере, который я тестировал, я думаю, что это проблема с Firefox. У меня есть reported it as a bug для Firefox, но я хочу убедиться, что это не просто что-то очевидное. Я делаю неправильно.

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

  1. Просмотр для текстового файла, который имеет 10 символов в нем (10 не магическое число - я просто использовать его в качестве примера)
  2. Заметим, что результат массив из 10 элементов, представляющих коды символов каждого элемента
  3. Пока он все еще запущен, удалите 5 символов из файла и сохраните
  4. Обратите внимание, что результат по-прежнему представляет собой массив из 10 элементов - первые 5 верны, но последние 5 - все 90 (прописная буква Z)
  5. Добавлено 10 символов (так что теперь файл составляет 15 символов cters длинные)
  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/

+0

Firefox может иметь проблемы с этим действительно ... При попытке загрузить скрипку разбился мой еженощно. Теперь это работает. – Oriol

+0

@Oriol В рамках тестирования этого небольшого репродуктора и моего большего приложения, использующего эту логику, я столкнулся с множеством сбоев в Firefox. :-(Я не использую Firefox (за исключением проверки совместимости с кросс-браузером), поэтому я не был уверен, что Firefox просто глючит вообще, глючит с FileReader или что. _Не оскорбление, предназначенное для поклонников Firefox_ –

+0

Возможно, связано с фактом что FF не обновляет «файлы» ввода, если имя одного и того же (нет события onchange) – Kaiido

ответ

7

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

Вы можете обратиться к этому link, заменив readAsArrayBuffer с пользовательской функциональностью, которая использует readAsBinaryString. его рабочий тон в FF и хроме

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

К сожалению, readAsBinaryString не поддерживается даже в последней версии IE. См. http://caniuse.com/#search=filereader. Возможно, я мог бы применить ваше обходное решение только тогда, когда существует readAsBinaryString (в конце концов, readAsArrayBuffer похоже, отлично работает в IE) –

+2

Я обновил этот ответ, включив в него проверку существования 'readAsBinaryString'. Это позволяет этому коду работать в IE. Теперь он выполняет все, что мне нужно - он работает s во всех браузерах, которые мне нужны, и легко просто вставить мою существующую базу кода, не меняя ничего. Это также будет легко удалить в будущем, если/когда исправлена ​​ошибка Firefox. –

3

Я думаю, что вы попадаете в ошибку 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: Весь API FileReader не поддерживается.
  • Практически все другие браузеры: Использует readAsBinaryString.
+1

Спасибо, ваше решение хорошее. Я принял [Pavan's] (http://stackoverflow.com/a/32343734/385996), потому что этот код легко просто добавить в существующее приложение, не изменяя ничего. Поскольку это (надеюсь) временная ошибка для обхода, наличие простого исправления для удаления - это то, что мне нужно. –

Смежные вопросы