Search code examples
javascriptimagepromisedimensions

How to get multiple image file infos using promise in javascript


I am Using promise to get dimensions of multi uploaded image files. This is what I tried but I got empty array.

<input type="file" multiple>

Javascript file

$('input').change(function() {

    var filereaders = [];
    var dimensions = [];
      for (var i = 0; i < this.files.length; ++i) {
        filereaders[i] = new FileReader;
        filereaders[i].onload = function() {
          var img = new Image;

          dimensions[i] = new Promise(function (resolve, reject) {
            img.onload = function() {
                    resolve(img.width)
            };
          })      
          img.src = this.result;
        } 

        filereaders[i].readAsDataURL(this.files[i]);
      }

      Promise.all(filereaders).then(function(res){
          Promise.all(dimensions).then(function(dims){
            console.log(dims) // [] array,I expect [23,434,123]...
          }).catch(function(errdims){
            console.log(errdims)
          })
      }).catch(function(err){
        console.log(err)
      })
});

Solution

  • Promise.all(filereaders)
    

    filereaders is an array of FileReader instances, not an array of Promises. Calling Promise.all on this array will immediately resolve the Promise, before any fileReader.onload event handlers have fired. Therefore, the dimensions array is still empty at this point. Now we get to Promise.all(dimensions), and it's an empty array.

    For your first Promise.all call, you could wrap your FileReaders in Promises and resolve them onload:

    var filereaders = []
    for (var i = 0; i < this.files.length; ++i) {
      var file = this.files[i]
      filereaders.push(new Promise(function (resolve, reject) {
        var fileReader = new FileReader;
        fileReader.onload = function() {
          var img = new Image;
    
          dimensions[i] = new Promise(function (resolve, reject) {
            img.onload = function() {
              resolve(img.width)
            };
          })      
          img.src = this.result;
    
          resolve();
        } 
    
        fileReader.readAsDataURL(file);
      }))
    }
    

    Now, filereaders is an array of Promises, so you can use Promise.all on it.

    There are some other things that could be done to simplify this code, though. File objects in JavaScript are Blobs, and Blobs can be added as Image .srcs without first reading them with a FileReader. Use URL.createObjectURL:

    var dimensions = [];
    for (var i = 0; i < this.files.length; ++i) {
      var file = this.files[i];
      dimensions.push(new Promise(function (resolve, reject) {
        var src = URL.createObjectURL(file);
        var img = new Image;
        img.onload = function () {
          resolve(img.width);
          URL.revokeObjectURL(src);
        };
        img.src = src;
      }));
    }
    

    Now we only need to wait for Promise.all(dimensions), and we can forget about filereaders entirely.

    Promise.all(dimensions).then(function(dims){
      console.log(dims) // [] array,I expect [23,434,123]...
    }).catch(function(errdims){
      console.log(errdims)
    })