1

I need to upload a canvas (saved as a png image) via jQuery Ajax. If I upload an image via a multipart form, where the user browses to a file than hits a submit button it works, but I need to capture a canvas and upload the image where the user is not uploading a file, but rather hitting a button to have the canvas converted than uploaded.

Here is an example form submit that uploads a file once a user browses and uploads their file:

<form action="http://myrestservice/endpoint" name="form" id="formId" method="post" enctype="multipart/form-data">
<input type="file" name="File1" size="50" maxlength="50" /><a class="textLabel">*** Upload a file is required</a>
<input type="submit" value="Submit Request">
</form>

This is only to demonstrate that the Web Service works. It states that the type of transfer is Document. I also get a 200 OK response.

If I create a different page with a canvas and add a button to upload the canvas to an image, it states that the type of transfer is xhr. I also get a 200 OK response.

Here is my JavaScript for uploading a canvas once it's converted to an image. What is interesting is that this code gives me a "not found in Access-Control-Allow-Origin header." error, while the regular manual upload does not:

$scope.imageMe = function () {
    var canvasImage = document.getElementById("c");
    var img = canvasImage.toDataURL("image/png");
    var filename = 'Test.png';
    var formdata = new FormData();
    formdata.append(filename, img);

    $.ajax({
        url: 'http://myrestservice/endpoint',
        type: "POST",
        data: formdata,
        processData: false,
        contentType: false,
        success: function (result) {
            console.log("Upload complete!");
        },
        error: function (error) {
            console.log("Something went wrong!");
        }            
    });
}

UPDATE:

I am able to post, but I don't get a response, as I get a cross origin error. The application vendor has replied with:

The page where you want to use the Ajax call, needs to send a correct access-control-allow-origin header. (this needs to be done by the webserver)

How/where do I add this in the AJAX to allow a cross origin call since the Web Service is in another domain?

UPDATE 2:

The vendor has responded with the following requirements. Given this, how do I code my AJAX request to work?

The HTTP POST requests must meet the following requirements:

  • The value of the Content-Type header of the HTTP POST request should be, in most cases, multipart/form-data.
  • HTTP request parts can be added to the HTTP POST requests to attach data files and/or to add parameters.
  • For every HTTP request part that is added to an HTTP POST request, a Content-Disposition header should be present.
  • For each data file, the (UTF-8 encoded) file name of the data file should be specified in the corresponding Content-Disposition header.
  • For each parameter, the name of the parameter should be specified in the corresponding Content-Disposition header.
  • The encoding of the HTTP POST requests should be base64 or no encoding (for example binary, 8bit,…).
Kode
  • 3,073
  • 18
  • 74
  • 140

2 Answers2

3

If you need to upload this as a file, e.g accessible in php by $_FILES['your_file'], you need to first convert it to a blob.

Unfortunately, canvas element's toBlob method is only implemented in Firefox, so I had to tweak this great answer. (You can use it if you need to convert to jpeg).

$scope.imageMe = function () {

     var send= function(blob){
        var filename = 'Test.png';
        var formdata = new FormData();
        formdata.append('your_file', blob, filename);

        $.ajax({
           url: 'http://myrestservice/endpoint',
           type: "POST",
           data: formdata,
           processData: false,
           contentType: false,
           success: function (result) {
              console.log("Upload complete!");
           },
           error: function (error) {
              console.log("Something went wrong!");
           }
        })            
    }

    var canvasImage = document.getElementById("c");
    if(!canvasImage.toBlob){
       var dataURL = canvasImage.toDataURL();
       var bytes = atob(dataURL.split(',')[1])
       var arr = new Uint8Array(bytes.length);
       for(var i=0; i<bytes.length; i++){
          arr[i]=bytes.charCodeAt(i);
       }
       send(new Blob([arr], {type:'image/png'}));
    }
    else
       canvasImage.toBlob(send);
}

Now as to why you've got this "Access-Control-Allow-Origin" error, @Yotam Omer gave you a good answer.

Community
  • 1
  • 1
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • This approach makes a lot of sense. What is the 'your_file' placeholder for? – Kode Aug 27 '15 at 12:47
  • 1
    to name your form, and access it through `$_FILES['your_file']` – Kaiido Aug 27 '15 at 12:47
  • That helps. Using your code, I have a 200 OK message for a post, but the callback is failing on the access-control. I will take this up with the application vendor today – Kode Aug 27 '15 at 12:51
  • try to change `your_file` with `name` – Kaiido Aug 27 '15 at 12:53
  • No such luck (name is not defined anywhere). Is there a value it typically expects? – Kode Aug 27 '15 at 12:57
  • I've noticed that with a form upload (manual) it sends the type as document, while using the AJAX code above it sends the type as xhr – Kode Aug 27 '15 at 13:13
  • sorry, I meant `form` not `name` (the name of your HTML form) – Kaiido Aug 27 '15 at 13:14
  • I don't have a form. I have a canvas that on button click I want to send the canvas as an image akin to how a manual form posts when a user uploads their image – Kode Aug 27 '15 at 13:16
  • Well you said you are able to send a file thanks to the HTML form you pasted in your question. Was this code given from your application vendor? If so, try to set this name using this HTML form's name or even the ``'s name (`File1`) so : `formdata.append('File1', blob, filename);` or `formdata.append('form', blob, filename);`, I don't remember which one's name is used... – Kaiido Aug 27 '15 at 13:20
  • Apologies, I was trying to show how the file upload works from a form, but rendering a canvas doesn't, despite them being the same web service. I will edit my initial post. My canvas code does not use the manual upload form, as it was only there to show that Web service works – Kode Aug 27 '15 at 13:21
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/88097/discussion-between-kaiido-and-kode). – Kaiido Aug 27 '15 at 13:22
2

The Access-Control-Allow-Origin header error means your service doesn't allow cross-domain requests.

You can change this header like so (php):

 header("Access-Control-Allow-Origin: *");

Note that this will allow requests from any domain, for security reasons I would recommend replacing the * with the domain name you want to make requests from.

Yotam Omer
  • 15,310
  • 11
  • 62
  • 65
  • I am using JavaScript and don't have control of the server. Why does it work on the form upload and not the AJAX post? Can I have my AJAX post act like the form upload? – Kode Aug 26 '15 at 21:34
  • 1
    Ajax is different from regular posting because your user is still on the original website and not transferred to the service website. You should also try changing the dataType of your ajax to: "jsonp" – Yotam Omer Aug 26 '15 at 21:37
  • I tried that earlier and again just now. It gives me a 500 error: 500 Problem(1) handling mapping-request for /endpoint : org.w3c.dom.DOMException: INVALID_CHARACTER_ERR: An invalid or illegal XML character is specified. – Kode Aug 26 '15 at 21:40
  • can you make a jsfiddle? I will take a closer look. – Yotam Omer Aug 26 '15 at 21:41
  • I would like too, but the application vendor is very particular about their endpoints. – Kode Aug 26 '15 at 21:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/88046/discussion-between-yotam-omer-and-kode). – Yotam Omer Aug 26 '15 at 21:44
  • Thr vendor replied that I need to set the headers, but don't see where in AJAX I would do this to allow CORS: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS – Kode Aug 28 '15 at 11:23
  • 1
    When preforming jQuery ajax request you can add the `headers` object to your request.. put there anything your vendor requests. `$.ajax(....., headers: {....}, .....);`. Also, check [this link](http://www.bennadel.com/blog/2327-cross-origin-resource-sharing-cors-ajax-requests-between-jquery-and-node-js.htm) out. – Yotam Omer Aug 29 '15 at 12:08
  • I ended using a proxy, but never would have got there without your assistance – Kode Sep 02 '15 at 21:03