3

I'm trying to read and add multiple files to an array. I've already found out that readAsArrayBuffer is a asynchronous function, so i need to wait for previous uploads to end. I tried to use callbacks but i failed.

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

Here's js file:

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);
  });
}

So now i have new FileReader each iteration, i got rid from Failed to execute 'readAsArrayBuffer' on 'FileReader': The object is already busy reading Blobs error, now i have the same files in my array, although I selected different.

google chrome dev

1
  • 1
    fileList.push(reader.result); ... the problem is what reader will be (you fixed file using an IIFE, but not reader Commented Oct 13, 2017 at 1:01

2 Answers 2

8

Your issue is that reader is going to be clobbered - using .forEach will mean each iteration is safe in its own closure - no need for IIFE kludges either

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);
    });
}

However this does not guarantee any particular "order" of the $('#upload-list').append - because asynchronous code is asynchronouos

Using Promises (which CAN be polyfilled for older browsers) you could do something like

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;
    });
}

This will guarantee the order that $('#upload-list').append is called

Sign up to request clarification or add additional context in comments.

1 Comment

forEach worked for me, thanks! except i placed reader.readAsArrayBuffer(file); into forEach. i think, i understood that things with IIFE.
0

There are syntax errors at + file.name, and missing ) at .ready(), defined parameter and pass reader to 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>

6 Comments

@JaromandaX Do you suggest including code at Answer which passed reader to the IIFE?
Not at all. forEach will fix this without resorting to new and overused toys like async/await that break older browser compatibility for no benefit at all
@JaromandaX Would not. Would probably define only a single instance of FileReader as well
@JaromandaX Always wondered what that :p meant. Am out of any and all loops
it's a "tongue hanging out" smiley (a smiley is a text only emoji - not sure if you needed that explanation :p )
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.