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

Posted

技术标签:

【中文标题】在 Firefox 中对更改的文件使用 FileReader.readAsArrayBuffer()【英文标题】:Using FileReader.readAsArrayBuffer() on changed files in Firefox 【发布时间】:2015-11-19 20:07:32 【问题描述】:

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

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

我从readAsArrayBuffer 返回的ArrayBuffer 始终具有文件最初的长度。如果用户更改文件以使其更大,我不会得到原始大小之后的任何字节。如果用户更改文件以使其更小,则缓冲区仍然是相同的大小,并且缓冲区中的 'excess' 填充有字符代码 90(如果将其视为字符串,则为大写字母 'Z')。

由于这段代码非常简单,并且在我测试过的所有其他浏览器中都能完美运行,我认为这是 Firefox 的问题。我已经向 Firefox 发送了 reported it as a bug,但我想确保这不仅仅是我做错的事情。

可以通过以下代码 sn-p 重现该行为。您所要做的就是:

    浏览包含 10 个字符的文本文件(10 不是幻数 - 我只是将其用作示例) 观察结果是一个包含 10 个项目的数组,代表每个项目的字符代码 当它仍在运行时,从文件中删除 5 个字符并保存 观察结果仍然是一个包含 10 项的数组 - 前 5 项是正确的,但后 5 项都是 90(大写字母 Z) 现在添加了 10 个字符(因此文件现在是 15 个字符长) 观察结果仍然是一个包含 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>

如果 sn-p 不起作用,示例代码也可在此处作为 JSFiddle 获得:https://jsfiddle.net/Lv5y9m2u/

【问题讨论】:

Firefox 可能确实有问题...尝试加载小提琴使我的 Nightly 崩溃。不过,现在它可以工作了。 @Oriol 作为测试这个小型复制品和使用此逻辑的更大应用程序的一部分,我在 Firefox 中遇到了很多崩溃。 :-( 我不使用 Firefox(除了测试跨浏览器兼容性),所以我不确定 Firefox 是否只是一般的错误,FileReader 或什么错误。无意冒犯 Firefox 粉丝 可能与 FF 不更新输入的 files 如果名称相同(未触发 onchange 事件)这一事实有关 无法在夜间 34 重现 这是另一个错误报告:bugzilla.mozilla.org/show_bug.cgi?id=1260606 【参考方案1】:

有趣的是,即使文件被修改,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();

【讨论】:

不幸的是,即使是最新版本的 IE 也不支持 readAsBinaryString。请参阅:caniuse.com/#search=filereader。也许我只能在 readAsBinaryString 存在时应用您的解决方法(毕竟 readAsArrayBuffer 在 IE 中似乎工作正常) 我已经更新了这个答案,包括检查readAsBinaryString 是否存在。这让这段代码在 IE 中工作。它现在满足了我所需要的一切——它适用于我需要的所有浏览器,并且很容易直接放入我现有的代码库而无需更改任何内容。如果/当 Firefox 错误得到修复,它也将很容易在未来被删除。【参考方案2】:

我认为您遇到了 Firefox 的错误。但是,正如您所指出的,readAsArrayBuffer 在除 Firefox 之外的所有支持的浏览器中都能正常运行,而除 IE 之外的所有浏览器都支持 readAsBinaryString

因此,可以在 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 FileReader API。 几乎所有其他浏览器:使用readAsBinaryString

【讨论】:

谢谢,您的解决方案很好。我接受了Pavan's,因为该代码很容易放入现有应用程序中而无需更改任何内容。由于这是(希望)一个临时的错误解决方法,所以我需要一个简单的直接修复。

以上是关于在 Firefox 中对更改的文件使用 FileReader.readAsArrayBuffer()的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 djangocms-filer 选择图像文件

jquery.filer 编辑模式动态文件添加

JQuery Filer 在提交时发布文件

使用 cypress-file-upload 将文件上传到 jQuery.filer 不起作用

互联网浏览器

jQuery Filer:如何在我的表单上禁用/启用