在 Firefox 中对已更改的文件使用 FileReader.readAsArrayBuffer()

Using FileReader.readAsArrayBuffer() on changed files in Firefox

我 运行 使用 FileReader.readAsArrayBuffer 遇到了一个奇怪的问题,它似乎只影响 Firefox(我在当前版本 - v40 中测试过)。我不知道是我做错了什么还是 Firefox 的错误。

我有一些 JavaScript 使用 readAsArrayBuffer 读取在 <input> 字段中指定的文件。在正常情况下,一切正常。但是,如果用户在 <input> 字段中选择文件后修改文件,readAsArrayBuffer 会变得非常混乱。

我从 readAsArrayBuffer 返回的 ArrayBuffer 始终具有文件最初的长度。如果用户更改文件以使其变大,我不会得到原始大小之后的任何字节。如果用户更改文件使其变小,缓冲区仍然是相同的大小并且缓冲区中的 'excess' 填充字符代码 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 个字符长)
  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/

有趣,看起来 Firefox 正在缓存缓冲区大小,即使文件已修改。

您可以参考此 link,将 readAsArrayBuffer 替换为使用 readAsBinaryString 的自定义功能。它在 Firefox 和 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,否则返回 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);
});

工作fiddle:https://jsfiddle.net/Lv5y9m2u/6/

浏览器支持:

  • Firefox:使用 readAsBinaryString,没有问题。
  • IE >= 10: 使用支持的 readAsArrayBuffer
  • IE <= 9: 整个 FileReader API 不支持。
  • 几乎所有其他浏览器:使用readAsBinaryString