83

So I'm using google maps and I get the picture so it looks like this

<img id="staticMap"
        src="http://maps.googleapis.com/maps/api/staticmap?center=Brooklyn+Bridge,New+York,NY&zoom=13&size=600x300&maptype=roadmap
&markers=color:blue%7Clabel:S%7C40.702147,-74.015794&markers=color:green%7Clabel:G%7C40.711614,-74.012318
&markers=color:red%7Ccolor:red%7Clabel:C%7C40.718217,-73.998284&sensor=false">

I need to save it. I have found this:

function getBase64FromImageUrl(URL) {
    var img = new Image();
    img.src = URL;
    img.onload = function() {

        var canvas = document.createElement("canvas");
        canvas.width = this.width;
        canvas.height = this.height;

        var ctx = canvas.getContext("2d");
        ctx.drawImage(this, 0, 0);

        var dataURL = canvas.toDataURL("image/png");

        alert(dataURL.replace(/^data:image\/(png|jpg);base64,/, ""));

    };
}

But I get this problem:

Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': tainted canvases may not be exported.

I searched for fixes. I found a sample here How to use CORS but still I can't tie these 2 pieces of code together to make it work. Maybe I'm doing it the wrong way and there is a simpler way to do it? I'm trying to save this pic so that I can transfer the data to my server. So maybe someone did something like this and knows how to make .toDataURL() work as I need it?

Kirby
  • 15,127
  • 10
  • 89
  • 104
user3074343
  • 1,091
  • 1
  • 11
  • 14

8 Answers8

110

Unless google serves this image with the correct Access-Control-Allow-Origin header, then you wont be able to use their image in canvas. This is due to not having CORS approval. You can read more about this here, but it essentially means:

Although you can use images without CORS approval in your canvas, doing so taints the canvas. Once a canvas has been tainted, you can no longer pull data back out of the canvas. For example, you can no longer use the canvas toBlob(), toDataURL(), or getImageData() methods; doing so will throw a security error.

This protects users from having private data exposed by using images to pull information from remote web sites without permission.

I suggest just passing the URL to your server-side language and using curl to download the image. Be careful to sanitise this though!

EDIT:

As this answer is still the accepted answer, you should check out @shadyshrif's answer, which is to use:

var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;

This will only work if you have the correct permissions, but will at least allow you to do what you want.

Community
  • 1
  • 1
Prisoner
  • 27,391
  • 11
  • 73
  • 102
  • 1
    I'd take the remote alternative suggestion from @Prisoner further and propose making it the default behaviour. The server will almost certainly be able to receive the _img.src_ then *wget*/*cURL* the image quicker than the client could encode a local-origin image and POST it to the server. Perhaps, only if client-side authentication is required (i.e. your server's attempt ends with a `403`), you could fall-back to using `canvas.toDataURL(img)` in the hopes that it's being served from the same origin as the page. This default behaviour will also result in the original file (EXIF et al.) – Alastair Jan 28 '14 at 20:46
  • 4
    I have set my Google Cloud Storage bucket to "Access-Control-Allow-Origin: *" yet I still get this security error when trying to use the toDataURL() method. The image is being served to the Canvas via the URL generated from "getImageServingUrl" – Mr Pablo Mar 02 '15 at 12:37
  • 1
    Any solution yet? This answer blows. – Yeats May 26 '15 at 23:14
  • Great ! Thanks a lot for this answer ! – Kevin Vincent Aug 22 '16 at 09:58
  • 5
    TIL: It is crucial to set the `crossOrigin` attribute before setting `img.src`. – horstwilhelm Jan 24 '18 at 10:07
  • It doesn't work because, in my case, the server is not sending the CORS headers. Without the crossOrigin attribute I can load the images but using it I can't – Ionel Lupu Dec 09 '21 at 15:25
59

Just use the crossOrigin attribute and pass 'anonymous' as the second parameter

var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url;
Frank Tan
  • 4,234
  • 2
  • 19
  • 29
Shady Mohamed Sherif
  • 15,003
  • 4
  • 45
  • 54
  • 8
    To clarify, this will not work for servers that directly wont let the resource to you. That mean, that this will work only for public resources, or missconfigured servers. – m3nda Feb 16 '15 at 06:34
  • 3
    I've also had to add a timestamp to the image URL to avoid the storage server from responding with 304 without the Access-Control-Allow-Origin header. Like this `img.src = url + '?' + new Date().getTime();` – Kirby Oct 14 '15 at 21:14
  • didn't work for me in IE11 with images the use data-url source – pumbo Aug 04 '20 at 12:25
6

This method will prevent you from getting an 'Access-Control-Allow-Origin' error from the server you are accessing to.

var img = new Image();
var timestamp = new Date().getTime();
img.setAttribute('crossOrigin', 'anonymous');
img.src = url + '?' + timestamp;
Calvin Ferrando
  • 3,794
  • 3
  • 16
  • 26
4

Try the code below ...

<img crossOrigin="anonymous"
     id="imgpicture" 
     fall-back="images/penang realty,Apartment,house,condominium,terrace house,semi d,detached,
                bungalow,high end luxury properties,landed properties,gated guarded house.png" 
     ng-src="https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" 
     height="220"
     width="200"
     class="watermark">
m87
  • 4,445
  • 3
  • 16
  • 31
ct.tan
  • 2,355
  • 1
  • 11
  • 4
  • 4
    what is the reason of pointless value of `fall-back` attribute? – y.bregey Jun 13 '17 at 12:57
  • 1
    I believe the only reason this works is because of `crossOrigin="anonymous"` the rest is irrelevant. I'm also pretty sure `ng-src` is not native javascript. – Dane Brouwer Jan 28 '21 at 10:08
0

In my case I was using the WebBrowser control (forcing IE 11) and I could not get past the error. Switching to CefSharp which uses Chrome solved it for me.

Brad Mathews
  • 1,567
  • 2
  • 23
  • 45
0

I had the same error message. I had the file in a simple .html, when I passed the file to php in Apache it worked

html2canvas(document.querySelector('#toPrint')).then(canvas => {
            let pdf = new jsPDF('p', 'mm', 'a4');
            pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 0, 0, 211, 298);
            pdf.save(filename);
        });
-2

if the picture from the 3rd party site didn't set the header for cors ("access-control-allow-origin"), you can never download the picture file through chrome, even if you use the setAttribute('crossOrigin', 'anonymous'); Here are some suggestions

  1. hack chrome (use an extension, this will only work on your machine)
  2. proxy the image through a service running on your site. The browser will see the domain as your site. This requires your service to request the image from the 3rd party.
rob
  • 8,134
  • 8
  • 58
  • 68
JokerSora
  • 7
  • 3
-3

By using fabric js we can solve this security error issue in IE.

    function getBase64FromImageUrl(URL) {
        var canvas  = new fabric.Canvas('c');
        var img = new Image();
        img.onload = function() {
            var canvas1 = document.createElement("canvas");
            canvas1.width = this.width;
            canvas1.height = this.height;
            var ctx = canvas.getContext('2d');
            ctx.drawImage(this, 0, 0);
            var dataURL = canvas.toDataURL({format: "png"});
        };
        img.src = URL;
    }
Ganesan Js
  • 162
  • 1
  • 10