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?
313 Answers
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).
In the img tag set crossorigin to Anonymous.
<img crossorigin="anonymous" /> 7 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: ' // 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 } }) })What's more, don't forget to set the access-control-allow-origin: * or access-control-allow-origin: [your whitelist origins] in the response header if the tiles are requested in your own server.
Like this:More details, and this one
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
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 crossorigin="anonymous"> <source src="">
</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"
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(); }); 1 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..
5Check out CORS enabled image from MDN. Basically you must have a server hosting images with the appropriate Access-Control-Allow-Origin header.
<IfModule mod_setenvif.c> <IfModule mod_headers.c> <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$"> SetEnvIf Origin ":" IS_CORS Header set Access-Control-Allow-Origin "*" env=IS_CORS </FilesMatch> </IfModule>
</IfModule>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 = ""; // 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;
} 2 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 -gorsudo npm install live-server -gon a mac - Run
live-serverand 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.
6This 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();
}); 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(); 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.
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: