0

I am sending the content of a canvas to my API endpoint through ajax, and I would like to be able to wait for the response to come, before proceeding with the next function. My sending function looks like this:

function sendPicture(){
  var video = document.getElementById('video');
  var canvas = document.getElementById('canvas');

  var context = canvas.getContext('2d');
  if (width && height) {
    canvas.width = width;
    canvas.height = height;
    context.drawImage(video, 0, 0, width, height);

    var fd = new FormData();
    fd.append('video', null);

    var reso;
    canvas.toBlob(function(blob){
        fd.set('video', blob);
    }, 'image/jpeg');

    reso = $.ajax({
      url: "/img",
      type : "POST",
      processData: false,
      contentType: false,
      data : fd,
      dataType: "text",
    });

    return reso;
  }
}

The function is already working when the ajax statement is used within the toBlob callback, but doing so I do not have access anymore to the main scope in order to block the ajax promise. From the current version of the function I think it would be enough if I managed to extract the blob argument outside the callback scope, even though I would have expected that the fd.set('video', blob) statement would have already set the formData object from the scope outside where it was initially created.

Does anyone have better suggestions? How can I convert the canvas to the blob without the method with the callback? Or better, how could I fill the formData of the outlying scope?

Neo
  • 448
  • 7
  • 18

2 Answers2

0

Its unclear how you get width && height, but it shuld work like this, just create a function outside the callback and call it inside the callback, that way you can access the data from within the callback outside of it

     function sendPicture(){
          var video = document.getElementById('video');
          var canvas = document.getElementById('canvas');
        
          var context = canvas.getContext('2d');
          if (width && height) {
            canvas.width = width;
            canvas.height = height;
            context.drawImage(video, 0, 0, width, height);
        
 
        }
           var fd = new FormData();
            fd.append('video', null);
           
         var setBlobOutside = function (blob){
           fd.set('video', blob);
}
            var reso;
            canvas.toBlob(function(blob){
                setBlobOutside(blob);
            }, 'image/jpeg');
        
            reso = $.ajax({
              url: "/img",
              type : "POST",
              processData: false,
              contentType: false,
              data : fd,
              dataType: "text",
            });
        
            return reso;
          }
        }

Your var fd = new FormData(); is not receving any data, and you are not appending data to it, so it is empty.. you can do it like this fd.append('key1', 'value1'); or like this , new FormData([data]);

here is an example on how to send form data with a file async

<form id="formElem">
  <input type="text" name="firstName" value="John">
  Picture: <input type="file" name="picture" accept="image/*">
  <input type="submit">
</form>

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('/article/formdata/post/user-avatar', {
      method: 'POST',
      body: new FormData(formElem)
    });

    let result = await response.json();

    alert(result.message);
  };
</script>
Stefan Avramovic
  • 1,365
  • 2
  • 11
  • 20
  • it seems still not to set the form. Could it be that toBlob is asynchronous as well, and I have to explicitly wait for it (even thouhg it return null, and not a promise as far as I understand). For width and height they came originally from a global context, now I moved them to the signature of the sendPicture function. – Neo Nov 04 '20 at 08:12
  • PS I also tried to remove the if statement, just to check if this was messing up with scopes in general, since I have seen that you move the respective close parenthesis upwards, but still fd does not get set. – Neo Nov 04 '20 at 08:14
0

In the end I used a promise constructor, inspired by access blob value outside of canvas.ToBlob() async function . In the following the solution I implemented:

function sendPicture(width, height){
  var video = document.getElementById('video');
  var canvas = document.getElementById('canvas');

  var context = canvas.getContext('2d');
  canvas.width = width;
  canvas.height = height;
  context.drawImage(video, 0, 0, width, height);

  return new Promise(function(resolve, reject) {
    canvas.toBlob(function(blob) {
      var fd = new FormData();
      fd.set('video', blob);

      $.ajax({
        url: "/img",
        type : "POST",
        processData: false,
        contentType: false,
        data : fd,
        dataType: "text",
      })
      .done(function(respPost) {
        resolve(respPost)
      })
      .fail(function(data) {
        console.log(data);
      });
    })
  })
}

which then is called within html code:

  <script>
    $(function() {
      var htmltakebutton = document.getElementById("takebutton");
      htmltakebutton.addEventListener('click', function(ev){
        var promiseSendPicture = sendPicture(250, 250);

        promiseSendPicture
        .then(function(respPost){
          console.log(respPost);
          updateTakenPhoto();
        });
        ev.preventDefault();
      }, false);
    })()
  </script>
Neo
  • 448
  • 7
  • 18