带有结果和参数的 FileReader onload

Posted

技术标签:

【中文标题】带有结果和参数的 FileReader onload【英文标题】:FileReader onload with result and parameter 【发布时间】:2015-01-31 00:19:16 【问题描述】:

我无法同时获得文件读取器的结果和 onload 函数中的一些参数。这是我的代码:

控件的html

<input type="file" id="files_input" multiple/>

javascript 函数:

function openFiles(evt)
    var files = evt.target.files;
    for (var i = 0; i < files.length; i++) 
      var file=files[i];
      reader = new FileReader();
      reader.onload = function()
          var data = $.csv.toArrays(this.result,separator:'\t');
      ;
      reader.readAsText(file);
    
  

添加事件:

 files_input.addEventListener("change", openFiles, false);

我在onload 函数中使用filereader.result。如果我为此函数使用参数(如文件),我将无法再访问结果。例如,我想在 onload 函数中使用file.name。如何解决这个问题?

【问题讨论】:

它是否适用于一个文件?尝试创建不同的阅读器,添加“var reader = new FileReader();” 使用一个简单的规则:在闭包的开头声明所有的局部变量。不要在forin 和其他类似语句中声明它们,因为这些语句不会创建闭包,因此声明的变量不是语句的本地变量。这将帮助您确定何时需要创建额外的闭包。 【参考方案1】:

使用

var that = this;

在函数范围内访问外部变量。

function()
    that.externalVariable //now accessible using that.___

我的场景 - 使用 Angular 9。 我为此苦苦挣扎了很长时间,我似乎无法让它发挥作用。 我发现以下是访问function() 块内的外部变量的一个非常优雅的解决方案。

public _importRawData : any[];

importFile(file)
    var reader = new FileReader();
    reader.readAsArrayBuffer(file);

    var data;
    var that = this;  //the important bit

    reader.onloadend = await function()
        //read data

        that._importRawData = data;  //external variables are now available in the function
    

上述代码中的一个重要部分是var 关键字,它将变量范围限定在功能块之外。 但是,当我在函数块之后访问data 的值时,它仍然未定义为在其他代码之后执行的函数。我尝试了异步和等待,但无法让它工作。而且我无法在这个函数之外访问data

可取之处是var that = this 行。 使用它允许在函数内部访问外部变量。所以我可以在函数范围内设置该变量,而不必担心代码何时执行。一经阅读,即可使用。

对于原始问题,代码为:

function openFiles(evt)
    var files = evt.target.files;
    for (var i = 0; i < files.length; i++) 
        var file=files[i];
      
        var that = this; //the magic happens
      
        reader = new FileReader();
        reader.onload = function()
            var data = $.csv.toArrays(this.result,separator:'\t');
          
            that.file.name //or whatever you want to access.
        ;
        reader.readAsText(file);
    

【讨论】:

【参考方案2】:

对于打字稿;

for (var i = 0; i < files.length; i++) 
  var file = files[i];

  var reader = new FileReader();

  reader.onload = ((file: any) => 
    return (e: Event) => 
      //use "e" or "file"
    
  )(file);

  reader.readAsText(file);

【讨论】:

【参考方案3】:

尝试将您的 onload 函数包装在另一个函数中。在这里,闭包让您可以通过变量 f 依次访问正在处理的每个文件:

function openFiles(evt)
    var files = evt.target.files;

    for (var i = 0, len = files.length; i < len; i++) 
        var file = files[i];

        var reader = new FileReader();

        reader.onload = (function(f) 
            return function(e) 
                // Here you can use `e.target.result` or `this.result`
                // and `f.name`.
            ;
        )(file);

        reader.readAsText(file);
    

有关为何需要在此处关闭的讨论,请参阅以下相关问题:

JavaScript closure inside loops – simple practical example Javascript infamous Loop issue?

【讨论】:

如果浏览器有FileReader,我认为它支持forEach和bind :) 我认为最好使用bind来添加文件到onload回调 @Pinal 这是个好建议。我不会更新我的答案以包含这些,因为我想让我的答案尽可能接近 OP 提供的代码,并且只更改必要的内容(引入闭包),但最好了解这些功能。 类似的徒劳无功,在生产代码中,我很想将 FileReader 代码移动到它自己的从循环调用的命名函数中。闭包会按预期运行,它有助于分解代码。也就是说,给出的示例更适合演示技术解决方案。 如果可以的话,我会为这个答案投票 1000 次。谢谢! 谢谢伙计!帮了我很多!【参考方案4】:

事件处理是异步的,因此它们会获取所有封闭局部变量的最新值(即闭包)。要将特定的局部变量绑定到事件,您需要按照上面用户建议的代码进行操作,或者您可以查看以下工作示例:-

http://jsfiddle.net/sahilbatla/hjk3u2ee/

function openFiles(evt)
  var files = evt.target.files;
  for (var i = 0; i < files.length; i++) 
    var file=files[i];
    reader = new FileReader();
    reader.onload = (function(file)
      return function() 
        console.log(file)
      
    )(file);
    reader.readAsText(file);
  


#Using jQuery document ready
$(function() 
  files_input.addEventListener("change", openFiles, false);
);

【讨论】:

这不太对。您应该为reader.onload 分配一个函数。您正在调用的 IIFE 不返回任何内容,因此一旦阅读器触发其加载事件,就不会运行任何代码。 我不明白为什么返回一个函数很重要,当这段代码只在 onload 事件上运行并且文件绑定到它时 目前,您的函数 (function(file)console.log(file))(file); 为您的 for 循环的每次迭代运行一次。它不返回任何东西,所以 reader.onload = undefined 用于循环的每个交互。因此,虽然您的console.log 确实打印了正确的文件名,但当reader 触发它的load 事件时,什么都不会运行。 感谢您强调,这是我的错,我应该解决这个问题【参考方案5】:

由于变量 file 在范围内,您可以使用 file 变量而不将其传递给函数。

function openFiles(evt)
    var files = evt.target.files;
    for (var i = 0; i < files.length; i++) 
          var file=files[i];    
          reader = new FileReader();
          reader.onload = function()
              alert(file.name);
              alert(this.result);
          ;
      reader.readAsText(file);
    
  

files_input.addEventListener("change", openFiles, false);
&lt;input type="file" id="files_input" multiple/&gt;

【讨论】:

谢谢,明白了:) 但是当我通过显示每个文件名和内容进行测试时,这似乎有效。运行上面的代码片段。我想我错过了一些会看到的东西。 上述代码在这种情况下有效,因为 onload 事件作为迭代的一部分发生(reader.readAsText)。而在***.com/questions/1451009 onclick 事件在迭代完成后发生 虽然这似乎可行,但当您运行上述 sn-p 并选择多个文件时,列表中的最后一个文件会显示多次。所以这不能按预期工作。【参考方案6】:

您应该在“onload”处理程序中使用闭包。 示例:http://jsfiddle.net/2bjt7Lon/

reader.onload = (function (file)  // here we save variable 'file' in closure
     return function (e)  // return handler function for 'onload' event
         var data = this.result; // do some thing with data
     
)(file);

【讨论】:

以上是关于带有结果和参数的 FileReader onload的主要内容,如果未能解决你的问题,请参考以下文章

Java的 FileWriter类 和 FileReader类

js 移动端 多图上传 预览 传给后端

3分钟上手JS中的FileReader对象(实现上传图片预览)

带有 FileReader() 的 Javascript 承诺

HTML5 FileReader 如何返回结果?

FileInputStream 与 FileReader