受污染的帆布不得出口

我想将画布保存到img。我有这样一个函数:

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

它给出了错误:

Uncaught SecurityError:未能在“HTMLCanvasElement”上执行“toDataURL”:受污染的画布可能无法导出。

我该怎么办?

356544 次浏览

似乎您正在使用的图像来自一个URL,没有设置正确的访问控制-允许起源头,因此出现了问题。您可以从您的服务器获取该映像,并从您的服务器获取它以避免CORS问题。

出于安全原因,您的本地驱动器被声明为“;other-domain"而且会污染画布。

(这是因为你最敏感的信息可能在你的本地驱动器上!)

在测试时尝试以下变通方法:

  • 将所有与页面相关的文件(.html, .jpg, .js, .css等)放在桌面上(不要放在子文件夹中)。

  • 将你的图片发布到支持跨域共享的网站(如dropbox.com或GitHub)。确保你把你的图片放在dropbox的公共文件夹中,并在下载图片时设置交叉原点标志(var img=new Image(); img.crossOrigin="anonymous"…)

  • 在你的开发计算机上安装一个web服务器(IIS和PHP web服务器都有免费版本,可以在本地计算机上很好地工作)。

在img标签中将crossorigin设置为Anonymous。

<img crossorigin="anonymous" />

如果你正在使用ctx.drawImage()函数,你可以做以下事情:

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;
}

在你的回调中,你现在可以使用ctx.drawImage并使用toDataURL导出它

如果有人对我的回答有看法,你可能会有这样的情况:

< p > < em > 1。尝试在画布中使用openlayers (version >= 3)
获取地图截图 2. 并查看了出口地图
的示例 3.使用ol.source.XYZ渲染地图层

宾果!

使用ol.source.XYZ.crossOrigin = '匿名'来解决你的困惑。 或者像下面的代码:

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

在OpenLayers6中,ES6做了一些改变。然而,代码是相似的。

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
}
})
})
< p > 更重要的是,不要忘记在响应头中设置__ABC0或access-control-allow-origin: [your whitelist origins] ,如果tile是在您自己的服务器中请求的。
是这样的: enter image description here 更多细节,和这一个

从MDN检查启用CORS的映像。 基本上,你必须有一个服务器托管图像和适当的Access-Control-Allow-Origin头

<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>

您将能够将这些图像保存到DOM存储,就像它们是从您的域提供的一样,否则您将遇到安全问题。

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;
}

在我的例子中,我从一个视频中用canvas.drawImage(video, 0, 0)之类的东西绘制到一个画布标签上。为了解决受污染的画布错误,我必须做两件事:

<video id="video_source" crossorigin="anonymous">
<source src="http://crossdomain.example.com/myfile.mp4">
</video>
  • 确保在视频源响应中设置Access-Control-Allow-Origin报头(crossdomain.example.com的正确设置)
  • 将video标签设置为crossorigin="anonymous"

我使用useCORS: true选项解决了这个问题

 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();
});

我还通过在我的代码中添加useCORS : true,解决了这个错误,比如-

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

就像@mark的回答一样。你可以通过本地服务器服务你的网站。在本地服务器上不会出现此错误。

如果你的电脑上已经安装了PHP(一些较旧的MacOS版本已经预安装了PHP):

  1. 打开终端/cmd
  2. 导航到你的网站文件所在的文件夹
  3. 在此文件夹中,运行命令php -S localhost:3000
  4. 打开浏览器,在URL栏找到localhost: 3000。你的网站应该在那里运行。


如果你的电脑上安装了Node.js:

  1. 打开终端/cmd
  2. 导航到你的网站文件所在的文件夹
  3. 在此文件夹中,运行命令npm init -y
  4. 在mac上运行npm install live-server -gsudo npm install live-server -g
  5. 运行live-server,它会自动在浏览器中打开一个新选项卡,同时打开你的网站。

注意:记得在你的文件夹的根目录下有一个index.html文件,否则你可能会遇到一些问题。

在我的情况下,我从我的桌面进行测试,即使在本地保存图像到子文件夹后,也有CORS错误。

解决方案:

移动文件夹到本地服务器WAMP在我的情况下。从本地服务器完美工作。

<强>注意:

这个可以在laravel中顺利工作。

首先,您需要将受污染的画布转换为blob。在此之后,您可以上传一个blob来服务并将其保存为图像。在ajax调用中返回图像URL。

这是一个上传画布blob的ajax调用。

$("#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();
});
对于任何在应用服务器跨源设置后仍然从S3遇到相同问题的人来说,这可能是浏览器缓存问题。 所以你需要确保禁用缓存并再次测试,你可以从浏览器dev-tools ->网络TAB ->点击禁用现金选项->再试一次:< / p >

chrome dev-tools caching option

博士tl;

这个问题让我疯狂,并通过加载图片与crossOrigin="匿名"渲染画布之前解决了它。

详细和过于具体的解决方案

对于那些使用React + canvg + Amazon S3并希望通过canvas将svg导出为png的人来说,这可能很有用。

首先,创建一个React钩子来检测预加载的跨原点图像:

// useLoadCrossOriginImage.tsx


import { useReducer, useMemo } from 'react'


export function useLoadCrossOriginImage(imageUrls: string[]) {
const [count, increase] = useReducer((count) => count + 1, 0)


const render = () =>
imageUrls.map((url) => (
<img
src={url}
crossOrigin="anonymous"
onLoad={increase}
className="hidden"
/>
))


const loaded = useMemo(() => count === imageUrls.length, [count, imageUrls])


return {
render,
loaded,
}
}

然后,加载图像后,惰性渲染svg:

// ImagePreview.tsx


import { useLoadCrossOriginImage } from './useLoadCrossOriginImage'


// This is usually state from parent component
const imageUrls = [
'https://s3-ap-northeast-1.amazonaws.com/bucket/xxxxxxx.png',
'https://s3-ap-northeast-1.amazonaws.com/bucket/yyyyyyy.png',
]


export const ImagePreview = () => {
const { loaded, render } = useLoadCrossOriginImage(imageUrls)


return (
<div className="border border-slate-300" onClick={onClick}>
{render()}
{loaded && (
<svg xmlns="http://www.w3.org/2000/svg">
{imageUrls.map((imageUrl) => (
<image key={el.id} href={imageUrl} />
))}
</svg>
)}
<canvas className="hidden" />
</div>
)
}

最后,你可以将canvas元素转换为png:

const canvas = document.querySelector('canvas')!
const ctx = canvas.getContext('2d')!
const svg = document.querySelector('svg')!
const v = Canvg.fromString(ctx, svg.outerHTML, { anonymousCrossOrigin: true })

最后,S3的cors策略应该是这样的:

{
"CORSRules": [
{
"ID": "s3-cors-policy",
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]
}

请让"MaxAgeSeconds"为空。