Wait until all files are read asynchronously (File

2020-06-23 05:18发布

问题:

I have a page where the user can select a folder to upload files. Before sending the files, I need to read them and check the data. My code is organized as follows:

$( '#folder-select' ).on('change', getValidFileList);
var fileList = [];

var getValidFileList = function(event) {

    //Get the selected files
    files  = $( this ).get(0).files;

    for(var i=0; i<files.length; i++) {
        checkFile(files[i]);
    }
    //Do something with the final fileList
    console.log(fileList);
};

var checkFile = function(file) {
    var reader = new FileReader();
    reader.onload = function (event) {
        //Here I parse and check the data and if valid append it to fileList
    };
    reader.readAsArrayBuffer(file);

};

I would like to take the resulting fileList array to keep processing/displaying the uploaded files. I found that reader.onload() is called asynchronously, so the result of the console.log(fileList) after the for loop is an empty array (it is executed before the reader.onload() is fired). Is there any way to wait until all files are read and appended to fileList?

回答1:

Just keep track of how many files has been processed compared to how many files has been given:

function getValidFileList(files, callback) {

  var count = files.length;              // total number of files
  var fileList = [];                     // accepted files

  //Get the selected files
  for(var i = 0; i < count; i++) {       // invoke readers
    checkFile(files[i]);
  }

  function checkFile(file) {
    var reader = new FileReader();
    reader.onload = function(event) {
      var arrayBuffer = this.result;
      //Here I parse and check the data and if valid append it to fileList
      fileList.push(arrayBuffer);        // or the original `file` blob..
      if (!--count) callback(fileList);  // when done, invoke callback
    };
    reader.readAsArrayBuffer(file);
  }
};

The --count will subtract one per reader onload hit. When =0 (or !count) it invokes the callback. Notice that the array order may not be the same as the one from files[n] it this should matter.

Then invoke it like this:

$( '#folder-select' ).on('change', function() {
  getValidFileList(this.files, onDone)
});

function onDone(fileList) {
  // continue from here
}