使用FileReader和readAsArrayBuffer进行多文件读取

3

我正在尝试读取并将多个文件添加到数组中。我已经发现readAsArrayBuffer是一个异步函数,所以我需要等待之前的上传结束。我尝试使用回调函数,但失败了。

<form enctype="multipart/form-data">
  <input id="file-input" type="file" multiple="" accept="image/*">
</form>
<div id="upload-list"></div>

这是js文件:

var fileList = [];
$(document).ready(function() {
  function addFiles(files) {
    for (var i = 0; i < files.length; i++) {
      var file = files[i];
      var reader = new FileReader();
      reader.onloadend = (function(file) {
        return function() {
          fileList.push(reader.result);
          $('#upload-list').append('<div class="upload-list-item">' + file.name '</div>');
        }
      })(file);
      reader.readAsArrayBuffer(file);
      console.log(fileList);
    }
  }
  $('#file-input').on('change', function(e) {
    addFiles(e.target.files);
  });
}

所以现在每次迭代我都有一个新的FileReader,我摆脱了“Failed to execute 'readAsArrayBuffer' on 'FileReader': The object is already busy reading Blobs”错误,现在我的数组中有相同的文件,尽管我选择了不同的文件。

google chrome dev


1
fileList.push(reader.result); ... the problem is what reader will be (you fixed file using an IIFE, but not reader - Jaromanda X
2个回答

8

您的问题在于reader将被覆盖 - 使用.forEach将意味着每个迭代都在自己的闭包中安全 - 无需使用IIFE hack。

function addFiles(files) {
    // files is not a regular Array
    [].forEach.call(files, function(file) {
        var reader = new FileReader();
        reader.onloadend = function() {
            fileList.push(reader.result);
            $('#upload-list').append('<div class="upload-list-item">' + file.name + '</div>');
        }
        reader.readAsArrayBuffer(file);
    });
}

然而,这并不保证$('#upload-list').append的任何特定“顺序”——因为异步代码是异步的。

使用Promises(可以为旧浏览器提供polyfill),你可以这样做:

function addFiles(files) {
    return Promise.all([].map.call(files, function (file) {
        return new Promise(function (resolve, reject) {
            var reader = new FileReader();
            reader.onloadend = function () {
                resolve({ result: reader.result, file: file });
            };
            reader.readAsArrayBuffer(file);
        });
    })).then(function (results) {
        results.forEach(function (result) {
            $('#upload-list').append('<div class="upload-list-item">' + result.file.name + '</div>');
        });
        return results;
    });
}

这将保证$('#upload-list').append被调用的顺序


forEach 对我有用,谢谢!只是我把 reader.readAsArrayBuffer(file); 放进了 forEach 里。我想我理解了 IIFE 的事情。 - randomsuffer

0

+ file.name 处存在语法错误,并且在 .ready() 处缺少 ),定义参数并将 reader 传递给 IIFE。

var fileList = [];
$(document).ready(function() {
  function addFiles(files) {
    for (var i = 0; i < files.length; i++) {
      var file = files[i];
      var reader = new FileReader();
      reader.onloadend = (function(file, reader) {
        return function() {
          fileList.push(reader.result);
          $('#upload-list').append('<div class="upload-list-item">' + file.name + '</div>');
        }
      })(file, reader);
      reader.readAsArrayBuffer(file);
      console.log(fileList);
    }
  }
  $('#file-input').on('change', function(e) {
    addFiles(e.target.files);
  });
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form enctype="multipart/form-data">
  <input id="file-input" type="file" multiple="" accept="image/*">
</form>
<div id="upload-list"></div>


@JaromandaX,您是否建议在答案中包含将reader传递给IIFE的代码? - guest271314
完全不需要。使用forEach就可以解决这个问题,而不必使用像async/await这样的新玩具,这些新玩具没有任何好处,只会破坏旧版浏览器的兼容性。 - Jaromanda X
@JaromandaX 不会的。可能只定义一个 FileReader 实例。 - guest271314
@JaromandaX 总是想知道那个 :p 是什么意思。我已经不在任何循环中了。 - guest271314
1
这是一个“伸舌头”的笑脸表情(笑脸是一种仅由文本组成的表情符号 - 不确定您是否需要这个解释 :p) - Jaromanda X
@guest271314 对不起,我的错,我删除了一些代码中的额外内容以便在这里发布。 - randomsuffer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接