如何修复getImageData()错误画布已被跨原点数据污染



我的代码在本地主机上运行得很好,但在站点上不起作用。

我从控制台得到了这个错误,对于这行.getImageData(x,y,1,1).data:

Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. 

我代码的一部分:

jQuery.Event.prototype.rgb=function(){
var x =  this.offsetX || (this.pageX - $(this.target).offset().left),y =  this.offsetY || (this.pageY - $(this.target).offset().top);
if (this.target.nodeName!=="CANVAS")return null;
return this.target.getContext('2d').getImageData(x,y,1,1).data;
}

注意:我的图像url(src)来自子域url

正如其他人所说,您通过从跨源域加载来"污染"画布。

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

然而,您可以通过简单设置来防止这种情况:

img.crossOrigin = "Anonymous";

只有当远程服务器正确设置以下标头时,这才有效:

Access-Control-Allow-Origin "*"

使用"直接链接"选项时的Dropbox文件选择器就是一个很好的例子。我在oddprints.com上使用它将远程dropbox图像url中的图像收集到我的画布中,然后将图像数据提交回我的服务器。全部在javascript 中

我发现我必须使用.setAttribute('crossOrigin', ''),并且必须在URL的查询字符串中附加一个时间戳,以避免304响应缺少Access-Control-Allow-Origin标头。

这给了我

var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');

您将无法直接从另一台服务器将图像绘制到画布中,然后使用getImageData。这是一个安全问题,画布将被视为"污点"。

使用PHP将图像的副本保存到服务器,然后只加载新图像,这对你有用吗?例如,您可以将URL发送到PHP脚本并将其保存到服务器,然后将新文件名返回到您的javascript,如下所示:

<?php //The name of this file in this example is imgdata.php
$url=$_GET['url'];
// prevent hackers from uploading PHP scripts and pwning your system
if(!@is_array(getimagesize($url))){
echo "path/to/placeholderImage.png";
exit("wrong file type.");
}
$img = file_get_contents($url);
$fn = substr(strrchr($url, "/"), 1);
file_put_contents($fn,$img);
echo $fn;
?>

您可以将PHP脚本与一些ajax javascript一起使用,例如:

xi=new XMLHttpRequest();
xi.open("GET","imgdata.php?url="+yourImageURL,true);
xi.send();
xi.onreadystatechange=function() {
if(xi.readyState==4 && xi.status==200) {
img=new Image;
img.onload=function(){ 
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
}
img.src=xi.responseText;
}
}

如果之后在画布上使用getImageData,它将正常工作。

或者,如果你不想保存整个图像,你可以通过x&y坐标到PHP脚本,并计算该侧像素的rgba值。我认为有很好的库可以在PHP中进行这种图像处理。

如果你想使用这种方法,如果你需要帮助实现它,请告诉我

edit-1:peeps指出php脚本被暴露,并允许互联网恶意使用它。有一百万种方法可以处理这个问题,其中最简单的是某种URL混淆。。。我认为安全的php实践应该有一个单独的谷歌;P

edit-2:根据流行的需求,我添加了一个检查,以确保它是一个图像而不是php脚本(来自:php检查文件是否是图像)。

当我在本地测试代码时,我在Chrome上看到了这个错误。我切换到Firefox,不再看到错误。也许切换到另一个浏览器是一个快速解决方案。

如果您使用的是第一个答案中给出的解决方案,请确保在声明img变量(例如var img = new Image();)之后添加img.crossOrigin = "Anonymous";

在本地工作时,添加一个服务器

我在本地工作时也遇到过类似的问题。您的URL将是本地文件的路径,例如file:///Users/PeterP/Desktop/folder/index.html

请注意,我在Mac电脑上。

我通过在全球范围内安装HTTP服务器来解决这个问题。我曾经https://www.npmjs.com/package/http-server

步骤

  1. 全局安装:npm install http-server -g
  2. 运行服务器:http-server ~/Desktop/folder/

这些步骤假设您已经安装了node,否则运行npm命令就不会太远。

我的问题太糟糕了,我只对图像进行了base64编码,以确保不会出现任何CORS问题

您的问题是加载外部映像,即从另一个域加载映像。当您尝试访问画布上下文的任何数据时,这会导致安全错误。

您通过从跨原点域加载来"污染"画布。查看这篇MDN文章:

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

ImagecrossOrigin属性设置为Anonymous

let image = new Image();
// image.src = ...;
image.crossOrigin = `Anonymous`;

正如matt burns在回答中所说,您可能需要在托管问题图像的服务器上启用CORS。

如果服务器是Apache,可以通过将以下代码段(从这里)添加到VirtualHost配置或.htaccess文件来完成:

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

如果将其添加到VirtualHost,您可能也需要重新加载Apache的配置(例如,如果Apache在Linux服务器上运行,则为sudo service apache2 reload)

您可以将图像转换为数据字符串,因为使用图像源而不是实际的图像源。

[https://www.base64-image.de/][1] 将图像转换为数据字符串。转换并复制上述网站的字符串数据。set image.src=<copied_data_string>。

解决方案,将源图像URL转换为Base64数据并分配给img

例如,使用Axios

const getBase64 = async(url)=>{
try {
let image = await axios.get(url, { responseType: 'arraybuffer' });
let raw = Buffer.from(image.data).toString('base64');
return "data:" + image.headers["content-type"] + ";base64,"+raw;
} catch (error) {
console.log(error)
} 
}
var image = new Image()
image.src=getBase64(url)

画布没有跨来源依赖

我今天遇到了同样的问题,并通过下面的代码解决了它。

html代码:

<div style='display: none'>
<img id='img' src='img/iak.png' width='600' height='400' />
</div>
<canvas id='iak'>broswer don't support canvas</canvas>

js代码:

var canvas = document.getElementById('iak')
var iakImg = document.getElementById('img')
var ctx = canvas.getContext('2d')
var image = new Image()
image.src=iakImg.src
image.onload = function () {
ctx.drawImage(image,0,0)
var data = ctx.getImageData(0,0,600,400)
}

代码如上,不存在跨域问题。

我遇到了同样的问题,对我来说,它只是通过连接https:${image.getAttribute('src')}来工作

最新更新