1

I am trying to post javascript array of object in an ajax call but i get string value "[]". When i try to console.log the array lenght it says zero.

Following is the code i am using

var masterFileArray = [];  // where I will store the contents
function readMultipleFiles(evt) {
//Retrieve all the files from the FileList object
var files = evt.target.files;
if (files) {
    for (var i = 0, f; f = files[i]; i++) {
        var r = new FileReader();
        r.onload = (function (f) {
            return function (e) {
                var contents = e.target.result;
                masterFileArray.push({name:f.name, contents: contents, type:f.type, size:f.size}); // storing as object

            };
        })(f);
        r.readAsText(f);
    }
    console.log(masterFileArray);
    new Ajax.Request('fileupload.php', {
    method: 'post',
    parameters: {files: JSON.stringify(masterFileArray)},
    onSuccess: function(transport){
        var response = transport.responseText;
        console.log(response);
    }
});

} else {
    alert('Failed to load files');
   }
}
 document.getElementById('upfiles').addEventListener('change', readMultipleFiles, false);

Thats how it looks like on inspection

enter image description here

What i am doing wrong? Any help would be appreciated, Thank you.

Shaonline
  • 1,597
  • 6
  • 18
  • 36
  • 2
    Because you're adding the elements asynchronously in the `onload` function, basically. `readAsText` doesn't block and wait for the file to be read, so your code just proceeds immediately to making the request before the array is populated. – Karl Reid Oct 19 '17 at 15:56
  • You're doing the Ajax Request BEFORE your files have been read by the file reader. – Danmoreng Oct 19 '17 at 15:56
  • 1
    Is r.onload being fired? Console.log inside it to know that :) – Rômulo M. Farias Oct 19 '17 at 15:59
  • 1
    Probably read this question and its answers: https://stackoverflow.com/questions/34495796/javascript-promises-with-filereader – Danmoreng Oct 19 '17 at 16:01
  • @RômuloM.Farias Yeah it fires. I tested with two images got two logs. – Shaonline Oct 19 '17 at 16:02
  • Actually this one is more appropriate for your scenario: https://stackoverflow.com/questions/41906697/how-to-determine-that-all-the-files-have-been-read-and-resolve-a-promise You want to wait for all files to be read, before you do your ajax request. – Danmoreng Oct 19 '17 at 16:04
  • This looks stupid to me, You should just use [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData), append each file and not use FileReader at all. – Endless Oct 19 '17 at 19:16

2 Answers2

1

You can post after reading finished, Here introduced left_loaded_count to get the status of reading. Try like this.

var masterFileArray = [];  // where I will store the contents
var left_loaded_count = 0;
function readMultipleFiles(evt) {
//Retrieve all the files from the FileList object
var files = evt.target.files;
if (files) {
    for (var i = 0, f; f = files[i]; i++) {
        var r = new FileReader();
        r.onload = (function (f) {
            return function (e) {
                var contents = e.target.result;
                masterFileArray.push({name:f.name, contents: contents, type:f.type, size:f.size}); // storing as object
                left_loaded_count -= 1;
                if(left_loaded_count == 0)
                {
                 console.log(masterFileArray);
                 new Ajax.Request('fileupload.php', {
                  method: 'post',
                  parameters: {files: JSON.stringify(masterFileArray)},
                  onSuccess: function(transport){
                     var response = transport.responseText;
                     console.log(response);
                   }
                  });
                }
            };
        })(f);
        left_loaded_count += 1;
        r.readAsText(f);
    }    
} else {
    alert('Failed to load files');
   }
}
 document.getElementById('upfiles').addEventListener('change', readMultipleFiles, false);
artgb
  • 3,177
  • 6
  • 19
  • 36
1

readAsText() is an asynchronous operation, but you proceed with the AJAX call right away instead of waiting for the read operations to finish. That's why your console.log(masterFileArray) prints an empty array, when it runs none of the operations have finished and the array is still empty.

The best way to solve this is to wrap each file read operation in a promise and then proceed with the AJAX call once all these promises resolve.
Get rid of var masterFileArray = [] and change your code within the if (files) { ... } block to this:

  Promise.all(files.map(function(f) {
    return new Promise(function(resolve) {
      var r = new FileReader();
      r.onload = function (e) {
        var contents = e.target.result;
        resolve({name:f.name, contents: contents, type:f.type, size:f.size}); // resolve promise with object
      };
      r.readAsText(f);
    });
  })).then(function(masterFileArray) {
    // All promises have resolved and their results have been collected in masterFileArray
    console.log(masterFileArray);
    new Ajax.Request('fileupload.php', {
      method: 'post',
      parameters: {files: JSON.stringify(masterFileArray)},
      onSuccess: function(transport){
        var response = transport.responseText;
        console.log(response);
      }
    );
  });
Lennholm
  • 7,205
  • 1
  • 21
  • 30