Escher Escher - 5 months ago 54
Javascript Question

Unable to send blob payload in FormData

I'm playing around with the

FormData
API. Here is the fiddle I'm using - I'm trying to inject blobs into my form and submit via AJAX.

//ic0 and tc0 are canvases
//image0 and thumb0 are file inputs
function ajaxSubmit(){
var fd = new FormData(document.forms[0]);
ic0.toBlob(
function(blob){
fd.set("image0", blob, "image0.jpg");
}, "image/jpeg", 0.7);
tc0.toBlob(
function(blob){
fd.set("thumb0", blob, "thumb0.jpg");
}, "image/jpeg", 0.7);
var xhr = new XMLHttpRequest();
xhr.open("POST", "/ajax-upload/", true);
xhr.send(fd);
}


Browser behaviour is a little ... odd:

Chrome 50 (in Ubuntu)



Not able to do it, gives:

Failed blob:https%3A//fiddle.jshell.net/uuid4 to load resource: the server responded with 404


But I thought
FormData.set()
was supported now? It seems to work with non-blobs?

Firefox 46 (in Ubuntu)



Doesn't seem work if
FormData()
object is not initialized with a DOM object that already has the necessary file inputs (appended normal inputs are posted however).
FormData.set()
does not seem to work with file inputs and blobs (you'll note that
thumb0
is null in the payload despite calling
fd.set("thumb0", blob, "thumb0.jpg")
on it. You can verify that it's set by doing
console.log(fd.get("thumb0"))
just after setting. You'll also note that the payload of
image0
is incorrectly your original image, not the resized canvas image.




It's inconvenient not being able to customise your multipart
FormData
with javascript. I'm a javascript noob - am I just doing something completely wrong here, or are these browsers not correctly supporting the
FormData
API? How can I submit
image0
and
thumb0
correctly in my fiddle?




Edit: OK, going to bounty this. I know there are big jquery-dependent libraries like blueimp out there, and I think they base64 the data rather than transmitting as a file input (plus I want to avoid jquery). I only have to support the latest Chrome or Firefox for this project and I'm really hoping I can keep the code as clean as the
FormData
API seems to suggest I might be able to. Can anyone successfully grab an image off a canvas and insert it into the POST payload as a file? I'd like to be able to access it in
request.FILES
in django.

Answer

I think one thing you are missing here is the asynchronous nature of javascript. In your ajaxSubmit method -

function ajaxSubmit(){
    var fd = new FormData(document.forms[0]);
    ic0.toBlob(function(blob){
        fd.set("image0", blob, "image0.jpg");
    }, "image/jpeg", 0.7);                // this call is asynchronous
    tc0.toBlob(function(blob){
        fd.set("thumb0", blob, "thumb0.jpg");
    }, "image/jpeg", 0.7);                // this call is also asynchronous
    /*
       hence the below code may get executed before
          fd.set("image0", blob, "image0.jpg");
       this statement. which it does. 
       that is why the canvas files are not submitted.  
    */
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "/ajax-upload/", true);
    xhr.send(fd);
}

Try synchronising the code like this

function ajaxSubmit(){
   var fd = new FormData(document.forms[0]);
   ic0.toBlob(function(blob){
      fd.set("image0", blob, "image0.jpg");
      tc0.toBlob(function(blob){
         fd.set("thumb0", blob, "thumb0.jpg");
         console.log(blob);
         console.log("Just blobbed thumb0");

         var xhr = new XMLHttpRequest();
         xhr.open("POST", "fileuploadbackend.jsp", true);
         //xhr.setRequestHeader("X-CSRFToken", csrftoken);
         xhr.send(fd);
      }, "image/jpeg", 0.7);

   }, "image/jpeg", 0.7);
}

also as jornare has suggested you should comment out the line

URL.revokeObjectURL(img.src);

which is causing error in chrome. Invoke it after the loading image is done.

also you have multiple elements with same id,

<input id="image0" name="image0" type="file" />
<label for="image0">image0</label> 
<input id="image0" name="thumb0" type="file" />

this isn't creating problem in your code but the id's should be different.

UPDATE

Follow these links to get more insight on asynchronous behaviour of javascript and how AJAX request callbacks are handled.