1

I have a file upload designed to handle multiple images. Each image has an id—based on the order in which the images were uploaded—that I want to send along with the post request. So I iterate through my fileSelect.files and upload the image with an XMLHttpRequest.

If I use xhr.open('POST', ajaxURL, false); (with asynchronous set to false) there's no issue; my xhr.onload = function () {...}; correctly returns the server response for each uploaded image.

However, if I try using xhr.open('POST', ajaxURL, true);(with asynchronous set to true), the handler gets confused and will only output information about the last file uploaded.

I think this might be caused by calling a new xhr.open('POST', ...); for every iteration of the loop. Is there a good way to either make the handler work with asynchronous requests, or to append the form data in such a way that I only need to make one request? This is a problem because I want to make sure each upload has completed successfully.

My working javascript is

// image upload handler
$(fileSelect).change(function(){

   // get the information we need to upload an image
   var ajaxURL = $("#action").val();
   var CSRFToken = document.getElementsByName('csrfmiddlewaretoken')[0].value;
   var house_id = $("#house_id").val();
   console.info("Uploading files to " + ajaxURL + " with using house_id=" + house_id + " and a csrf token value of " + CSRFToken);

   // make an AJAX call to upload the image
   uploadImage(this, ajaxURL, CSRFToken, house_id);
});

/**
 * Upload each image file in the given fileSelect object
 * via an AJAX request to the specified URL using the provided
 * CSRF token. A house_id parameter must be provided for the database
 * to organize image uploads by house.
 */
var uploadImage = function(fileSelect, ajaxURL, CSRFToken, house_id) {
   // get the selected files from the input
   var files = fileSelect.files;

   // create the object resposible for handling the ajax request
   var xhr = new XMLHttpRequest();
   xhr.onload = function () {
      if (xhr.status === 200) {
     alert(xhr.statusText + ": " + xhr.responseText)
      } else {
     alert("Oops! The file upload failed. Please try again.");
      }
   };

   // loop through each of the selected files
   for (var i = 0; i < files.length; i++) {
      var file = files[i];

      // check the file type
      if (!file.type.match('image.*')) {
     // if it's not an image -> alert user & dont upload
     alert(file.name + " could not be added because it is of the wrong filetype.");
     continue;
      }

      console.log("Uploading " + file.name + " . . .");

      // create a new FormData object for our images
      var formData = new FormData();

      // append the current image and related values to our form data
      formData.append('file', file);
      formData.append('csrfmiddlewaretoken', CSRFToken);
      formData.append('house_id', house_id);
      formData.append('image_id', i);

      // make the request and set a handler
      xhr.open('POST', ajaxURL, false);
      xhr.send(formData);
   }

};
John Dorian
  • 1,884
  • 1
  • 19
  • 29
  • Just call everything except for `formData.append('file', file);` outside the loop. But the question is what does your server expect? – a better oliver Sep 02 '15 at 17:17
  • @zeroflagL Right now the server is expecting to find a file object in the post request as file. I can change the server implementation though. If I call everything outside the loop how can I make sure that different id's are associated with their respective images? `house_id` is the only parameter that should change between images. – John Dorian Sep 02 '15 at 17:41
  • 1
    You shall create a new `xhr` for every upload! – Bergi Sep 02 '15 at 17:46
  • @Bergi That's what I thought too! But If I move the block that follows the comment `// create the object resposible for handling the ajax request` (which calls `var xhr = new XMLHttpRequest();`) into the loop, the handler still gets confused and only outputs information about the last file upload. Can you see something I'm missing there? – John Dorian Sep 02 '15 at 17:53
  • 1
    @JohnDorian: yes, *then* you have the [classical handler closure in a loop](http://stackoverflow.com/q/750486/1048572) problem - in your case closing over the single `xhr` variable. – Bergi Sep 02 '15 at 17:58
  • @Bergi Thank you so much, that pointed me right where I needed to go in order to fix this issue. Do you want to post that as an answer so I can mark it as accepted? – John Dorian Sep 02 '15 at 18:32
  • `house_id` is the same for every image in your example. Ids are usually assigned by the server. One request per file is a viable alternative, of course, but based on the information you gave us, I see no compelling reason for that. It creates more traffic and makes the client-side code more complicated. So there should be an advantage in it for you to justify that alternative, imho. – a better oliver Sep 03 '15 at 07:22
  • @zeroflagL it's about `image_id`, that field is different for each image. – John Dorian Sep 03 '15 at 16:38
  • I know, but I don't see any reason that id should be assigned by the client. – a better oliver Sep 03 '15 at 17:17
  • @zeroflagL In this case, it's because id corresponds to the image's order in a set. Defined by the client. In a JQuery UI. And I want to post the results via ajax. – John Dorian Sep 04 '15 at 18:22

0 Answers0