Search code examples
javascriptfabricjs

Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported


Looks like the issue is related mostly to my aws s3 bucket CORS config

I added:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>https://www.example.com</AllowedOrigin>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

to my bucket CORS config, but this didn't help either. For some reason I am able to add the photos to the canvas, (depending on the code) but not able to save the canvas with the image from AWS


I have a fabric.js canvas that is being tainted on images from amazon s3. I am unsure on exactly what is going on.

When I try to save the canvas by clicking save:

Chrome Error on console: Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

FireFox Error on console: Operation is insecure

https://jsfiddle.net/je3mL5go/1/

In the jsfiddle, it works when the added line }, {crossOrigin: 'Anonymous'}); is present.

In production on Heroku with s3 Images:

In FireFox, I can select an image from the select menu and have it add to the canvas. I'll get the error as mentioned but if I repeatedly click the "Save" button, after enough clicks (usually 5-30), it will allow me to save the canvas as a png.

In Chrome, this "hack" doesn't seem to work at all.

Is there an explanation behind this?

It also seems that because I am hosting the images in AWS S3, this effects it as well. But even with this, the above example (repeat clicking in FireFox) still works.

Attempts:

With:

function updateTshirtImage(imageURL){
  fabric.Image.fromURL(imageURL, function(img) {
    img.scaleToHeight(300);
    img.scaleToWidth(300);
    img.crossOrigin = 'anonymous';
    canvas.centerObject(img);
    canvas.add(img);
    canvas.renderAll();
  });
};

I am able to upload the image but when i click save:

I get the same errors as above.

When i use:

function updateTshirtImage(imageURL){
  fabric.Image.fromURL(imageURL, function(img) {
     img.scaleToHeight(300);
     img.scaleToWidth(300);
     canvas.centerObject(img);
     canvas.add(img);
     canvas.renderAll();
  }, {crossOrigin: 'anonymous'});
};

Error on upload by select image

No 'Access-Control-Origin-Header' is present...

Attempt:

function updateTshirtImage(imageURL){
   var rand = '?'+Math.random();
   var no_cors = new Image();
   no_cors.onload = loadCORS;
   no_cors.src = imageURL + rand;

   function loadCORS(){
     var with_cors = new Image();
     with_cors.crossOrigin = 'anonymous';
     with_cors.src = no_cors.src;
     with_cors.onload = function(){console.log('loaded');};
     with_cors.onerror = function(){console.error('failed');};
     fabric.Image.fromURL(with_cors.src, function(img) {
     img.scaleToHeight(300);
     img.scaleToWidth(300);
     canvas.centerObject(img);
     canvas.add(img);
     canvas.renderAll();
   }, {crossOrigin: 'anonymous'});
  }
};

var images = <%= images_2.to_h.to_json.html_safe %>
document.getElementById("tshirt-design").addEventListener("change", function(){
    updateTshirtImage(images[this.value]);
}, false);

error:

Chrome: 404 error

Firefox: No error, not working.

Is the issue here is that I'm somehow changing the url for aws s3 before asking for it from aws?


Solution

  • The adding of:

    <?xml version="1.0" encoding="UTF-8"?>
    <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>https://www.example.com</AllowedOrigin>
        <AllowedMethod>PUT</AllowedMethod>
        <AllowedMethod>POST</AllowedMethod>
        <AllowedMethod>DELETE</AllowedMethod>
        <AllowedMethod>GET</AllowedMethod>
        <AllowedMethod>HEAD</AllowedMethod>
        <AllowedHeader>*</AllowedHeader>
    </CORSRule>
    </CORSConfiguration>
    

    Actually did the trick. Looks like I just needed to wait. The next day I tested and it worked!

    To clarify, the above header code is probably overkill but this is where I am currently at. I will be testing and "debugging" and removing some Allowed Methods and see which and which are not needed.