ChatGPT解决这个技术问题 Extra ChatGPT

Tainted canvases may not be exported

I want to save my canvas to a img. I have this function:

function save() {
    document.getElementById("canvasimg").style.border = "2px solid";
    var dataURL = canvas.toDataURL();
    document.getElementById("canvasimg").src = dataURL;
    document.getElementById("canvasimg").style.display = "inline";
}

It gives me error:

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

What should I do?

In what browser? stackoverflow.com/a/21362569/476716 claims this is a bug.
on chrome and on firefox

n
naXa stands with Ukraine

For security reasons, your local drive is declared to be "other-domain" and will taint the canvas.

(That's because your most sensitive info is likely on your local drive!).

While testing try these workarounds:

Put all page related files (.html, .jpg, .js, .css, etc) on your desktop (not in sub-folders).

Post your images to a site that supports cross-domain sharing (like dropbox.com or GitHub). Be sure you put your images in dropbox's public folder and also set the cross origin flag when downloading the image (var img=new Image(); img.crossOrigin="anonymous" ...)

Install a webserver on your development computer (IIS and PHP web servers both have free editions that work nicely on a local computer).


Thanks, setting the img.crossOrigin property helped me!
@markE - I loaded image data from localStorage instead of loading from file or any url, then did some manipulation to it like adding a text. Then tried to sotre back it to localStorage using toDataURL(). But it shows "Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported". In this case I am not using any exteranl file or url to get a cross domain issue. Then why it is resulting in this error?
@Saijth - You may want to verify the path used for the images. I had this problem as well because I was testing directly accesing my local virtual server thru its IP (127.0.x.x/) but some of the images were linked thru the domain (localhost/). Once I used the localhost instead it worked out. So make sure you arent running into something like that.
(1) View it from xampp webserver, localhost/file instead of c:/localdisk/file; then chrome won't complain about Security Error. (2) Or use this flag when starting chrome: --allow-file-access-from-files
Just adding another possible problem: if you're trying to export a canvas that contains a svg with a ForeignObject, some browsers will mark it as tainted.
R
Roko C. Buljan

In the img tag set crossorigin to Anonymous.

<img crossorigin="anonymous" />

But what to do in the case of html5 canvas , not img elements
In the case of a canvas element, the source of the problem is always with some image (or images) that you're drawing onto it. So you just need to track down the image and set its crossOrigin attribute as indicated, before loading it.
works well with html2canvas(element.nativeElement, {useCORS: true}) .then(canvas => { const dataUrl = canvas.toDataURL('image/png'); })
It's worth noting that setting image.crossOrigin = 'Anonymous' won't help once the image has been loaded. But if a reload is an option you can go for something like: document.querySelectorAll('img').forEach(image => { image.crossOrigin = 'Anonymous'; image.src += ' '; }). See also this article on MDN for reference.
This didn't work for me as all lowercase, but crossOrigin worked.
s
sknight

If someone views on my answer, you maybe in this condition:

1. Trying to get a map screenshot in canvas using openlayers (version >= 3)
2. And viewed the example of exporting map
3. Using ol.source.XYZ to render map layer

Bingo!

Using ol.source.XYZ.crossOrigin = 'Anonymous' to solve your confuse. Or like following code:

 var baseLayer = new ol.layer.Tile({
     name: 'basic',
     source: new ol.source.XYZ({
         url: options.baseMap.basic,
         crossOrigin: "Anonymous"
     })
 });

In OpenLayers6, something is changed with ES6. However, the code is similar.

import { XYZ } from 'ol/source'
import { Tile as TileLayer } from 'ol/layer'
const baseLayer = new TileLayer({
    name : 'basic',
    source: new XYZ({
      url: 'example.tile.com/x/y/z', // your tile url
      crossOrigin: 'Anonymous',
      // remove this function config if the tile's src is nothing to decorate. It's usually to debug the src
      tileLoadFunction: function(tile, src) {
        tile.getImage().src = src
      }
    })
  })

https://i.stack.imgur.com/qsgp8.png


fantastic. Useful for all sources, like TileImage and such as well.
Fatastic! Exactly what I was looking for myself, easy fix for an OpenLayers demo Im doing.
I'm using vector tile layers and after add this propery to source but it is not working too!
I'm using ol6. but its not working , No change after adding this line
@JayCummins, I add the OL6 code. It maybe helps you.
L
Lawrence Cherone

If you're using ctx.drawImage() function, you can do the following:

var img = loadImage('../yourimage.png', callback);

function loadImage(src, callback) {
    var img = new Image();

    img.onload = callback;
    img.setAttribute('crossorigin', 'anonymous'); // works for me

    img.src = src;

    return img;
}

And in your callback you can now use ctx.drawImage and export it using toDataURL


This did not work for me. Still getting the Tainted canvases may not be exported. error message.
it works for me. thanks. @SamSverko make sure set the attribute before img.src.
Doesn't work. Says blocked by CORS policy
as been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled. The FetchEvent for "img.
F
Frederick

In my case I was drawing onto a canvas tag from a video with something like canvas.drawImage(video, 0, 0). To address the tainted canvas error I had to do two things:

<video id="video_source" crossorigin="anonymous">
    <source src="http://crossdomain.example.com/myfile.mp4">
</video>

Ensure Access-Control-Allow-Origin header is set in the video source response (proper setup of crossdomain.example.com)

Set the video tag to have crossorigin="anonymous"


A
Ahmad Zahabi

I resolved the problem using useCORS: true option

 html2canvas(document.getElementsByClassName("droppable-area")[0], { useCORS:true}).then(function (canvas){
        var imgBase64 = canvas.toDataURL();
        // console.log("imgBase64:", imgBase64);
        var imgURL = "data:image/" + imgBase64;
        var triggerDownload = $("<a>").attr("href", imgURL).attr("download", "layout_"+new Date().getTime()+".jpeg").appendTo("body");
        triggerDownload[0].click();
        triggerDownload.remove();
    });

only solution here that works for me!
P
Prasanna Aarthi

Seems like you are using an image from a URL that has not set correct Access-Control-Allow-Origin header and hence the issue.. You can fetch that image from your server and get it from your server to avoid CORS issues..


Could you possible be more precise about your answer, because im not really know all that concept . How do i fetch that image from my server?
from where are you fetching that image, is it from your server or some other one?
It goes like this : loadImage("example.jpg", 0, 0, 500, 300); I can put random image url or image on the same folder in my computer, its still the same
yes, but i just created image on paint and it still is the same
I gave Access-Control-Allow-Origin:* for the file, but still it is showing the error
Z
Zsolt Meszaros

Check out CORS enabled image from MDN. Basically you must have a server hosting images with the appropriate Access-Control-Allow-Origin header.

SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS

You will be able to save those images to DOM Storage as if they were served from your domain otherwise you will run into security issue.

var img = new Image, canvas = document.createElement("canvas"), ctx = canvas.getContext("2d"), src = "http://example.com/image"; // insert image url here img.crossOrigin = "Anonymous"; img.onload = function() { canvas.width = img.width; canvas.height = img.height; ctx.drawImage( img, 0, 0 ); localStorage.setItem( "savedImageData", canvas.toDataURL("image/png") ); } img.src = src; // make sure the load event fires for cached images too if ( img.complete || img.complete === undefined ) { img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; img.src = src; }


While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From Review
Thanks for the notice. I just add the content from the MDN link:)
J
Jatin Mandanka

This one can work smoothly in laravel.

First of all, you need to convert tainted canvas to blob. after that, you can upload a blob to serve and save it as an image. Return image URL in ajax call.

Here is an ajax call to upload canvas blob.

$("#downloadCollage").click(function(){
  canvas.toBlob(function(blob){

    var formDataToUpload = new FormData();
    formDataToUpload.append("_token", "{{ csrf_token() }}");
    formDataToUpload.append("image",  blob);

    $.ajax({
        url:"{{ route('selfie_collage_upload') }}",
        data: formDataToUpload,
        type:"POST",
        contentType:false,
        processData:false,
        cache:false,
        dataType:"json",
        error:function(err){
            console.error(err);
        },
        success:function(data){
            window.location.href= data.url;
        },
        complete:function(){
        }
    });
  },'image/png');
  link.click();
});

Im getting an error "canvas is not defined" and I cant figure out how to solve it
@LaurențiuCozma here into my code, the canvas is a variable. for example canvas = document.getElementById('my-canvas');
L
Ludolfyn

Just as a build on @markE's answer. You can serve your website via a local server. You won't have this error on a local server.

If you have PHP installed on your computer (some older MacOS versions has it preinstalled):

Open up your terminal/cmd Navigate into the folder where your website files are While in this folder, run the command php -S localhost:3000 Open up your browser and in the URL bar go to localhost:3000. Your website should be running there.

or

If you have Node.js installed on your computer:

Open up your terminal/cmd Navigate into the folder where your website files are While in this folder, run the command npm init -y Run npm install live-server -g or sudo npm install live-server -g on a mac Run live-server and it should automatically open up a new tab in the browser with your website open.

Note: remember to have an index.html file in the root of your folder or else you might have some issues.


Mac OS does NOT have php pre-installed. I have macos and it has nothing about PHP. If you have php, you or someone else installed it.
That's not true @LukasLiesis. A simple Google search will show you that it is: google.com/…. All the Macs I've ever worked on had it preinstalled. If you have Big Sur installed as the default (not as an update), you might need to activate it (although I didn't have to): google.com/….
In the terminal you can run php --version to see what version you have installed.
Go get new mac, run your apache daemon script which is referenced in the article and you will see PHP is not part of mac. Unless you run some old version of OS, which is never a good idea, at least on mac. The line about Apache's php module is not even in httpd.conf, but seems like it was there on older OSes. There is a line with comment: #PHP was deprecated in macOS 11 and removed from macOS 12 Both of your linked articles are just wrong. php --version says command not found, while php is not part of mac :)
As a matter of fact I have a brand new macbook that I bought a month ago and it still has PHP preinstalled and activated. PHP worked right off the bat. The only thing I can note is that I chose an intel chip and not the M1 chip (for compatibility with some of the software I'm using). No one said it's part of mac. I said it comes preinstalled, which it does :) Although when you check the version of PHP in the terminal it does print a warning saying—and I quote: "Future versions of macOS will not include PHP." Here's a screenshot: imgur.com/a/ZZL1SwN
J
Jeet

I also solved this error by adding useCORS : true, in my code like -

html2canvas($("#chart-section")[0], {
        useCORS : true,
        allowTaint : true,
        scale : 0.98,
        dpi : 500,
        width: 1400, height: 900
    }).then();

A
Anil Singh

In my case I was testing it from my desktop, having CORS error even after saving image locally to sub-folder.

Solution:

Moved the folder to local server WAMP in my case. Worked perfect from local server.

Note: Works only when you have saved image locally.


Y
Yaser AZ

For anyone who still encountering the same issue from S3 even after applying the server cross-origin settings, it probably a browser caching issue. So you need to make sure to disable the caching and test again, you can do that from the browser dev-tools -> network tab -> click on disable cash option -> try again:

https://i.stack.imgur.com/PhZPl.png


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now