受污染的画布不得出口
Posted
技术标签:
【中文标题】受污染的画布不得出口【英文标题】:Tainted canvases may not be exported 【发布时间】:2014-05-07 18:50:06 【问题描述】:我想将我的画布保存到 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";
它给了我错误:
未捕获的安全错误:无法在“htmlCanvasElement”上执行“toDataURL”:可能无法导出受污染的画布。
我该怎么办?
【问题讨论】:
在什么浏览器中? ***.com/a/21362569/476716 声称这是一个错误。 在 chrome 和 firefox 上 developer.mozilla.org/en-US/docs/Web/HTML/… 【参考方案1】:出于安全原因,您的本地驱动器被声明为“其他域”并且会污染画布。
(那是因为您最敏感的信息可能在您的本地驱动器上!)。
在测试时尝试以下解决方法:
将所有与页面相关的文件(.html、.jpg、.js、.css 等)放在桌面上(而不是放在子文件夹中)。
将您的图片发布到支持跨域共享的网站(如 dropbox.com 或 GitHub)。确保将图片放在 Dropbox 的公用文件夹中,并在下载图片时设置跨源标志 (var img=new Image(); img.crossOrigin="anonymous"
...)
在您的开发计算机上安装网络服务器(IIS 和 php 网络服务器都有免费版本,可以很好地在本地计算机上运行)。
【讨论】:
谢谢,设置 img.crossOrigin 属性帮助了我! @markE - 我从 localStorage 加载图像数据,而不是从文件或任何 url 加载,然后对其进行了一些操作,例如添加文本。然后尝试使用 toDataURL() 将其还原到 localStorage。但它显示“无法在 'HTMLCanvasElement' 上执行 'toDataURL':可能无法导出受污染的画布”。在这种情况下,我没有使用任何外部文件或 url 来解决跨域问题。那为什么会导致这个错误呢? @Saijth - 您可能需要验证用于图像的路径。我也遇到了这个问题,因为我正在测试通过其 IP (127.0.x.x/) 直接访问我的本地虚拟服务器,但一些图像是通过域 (localhost/) 链接的。一旦我使用了本地主机,它就解决了。因此,请确保您没有遇到类似的情况。 (1) 从xampp webserver,localhost/file而不是c:/localdisk/file查看;那么chrome就不会抱怨安全错误。 (2) 或者启动chrome时使用这个标志:--allow-file-access-from-files 只是添加另一个可能的问题:如果您尝试导出包含带有 ForeignObject 的 svg 的画布,某些浏览器会将其标记为已污染。【参考方案2】:在 img 标签中设置 crossorigin 为 Anonymous。
<img crossorigin="anonymous" />
【讨论】:
但是在 html5 canvas 的情况下怎么办,而不是 img 元素 在画布元素的情况下,问题的根源总是与您正在绘制的一些图像(或图像)有关。因此,您只需要在加载之前跟踪图像并按照指示设置其 crossOrigin 属性。 适用于 html2canvas(element.nativeElement, useCORS: true) .then(canvas => const dataUrl = canvas.toDataURL('image/png' ); ) 值得注意的是,设置image.crossOrigin = 'Anonymous'
在图像加载后将无济于事。但是,如果可以选择重新加载,则可以选择:document.querySelectorAll('img').forEach(image => image.crossOrigin = 'Anonymous'; image.src += ' '; )
。另请参阅this article on MDN 以供参考。
这对我来说不是全小写的,但 crossOrigin
有效。【参考方案3】:
如果有人看我的回答,你可能处于这种情况:
1.尝试使用 openlayers(版本 >= 3)在画布中获取地图屏幕截图 2.查看exporting map的例子 3.使用ol.source.XYZ渲染地图层
宾果游戏!
使用 ol.source.XYZ.crossOrigin = 'Anonymous' 来解决您的困惑。 或者像下面的代码:
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
)
)
此外,如果您在自己的服务器中请求磁贴,请不要忘记在响应标头中设置access-control-allow-origin: *
或access-control-allow-origin: [your whitelist origins]
。
像这样:
更多details和this one
【讨论】:
太棒了。适用于所有来源,例如 TileImage 等。 太棒了!正是我在寻找自己的东西,很容易修复我正在做的 OpenLayers 演示。 我正在使用矢量切片图层,然后将此属性添加到源,但它也无法正常工作! 我正在使用 ol6。但它不起作用,添加此行后没有变化 @JayCummins,我添加了 OL6 代码。也许对你有帮助。【参考方案4】:如果您使用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
导出它
【讨论】:
这对我不起作用。仍然收到Tainted canvases may not be exported.
错误消息。
它对我有用。谢谢。 @SamSverko 确保在 img.src 之前设置属性。
不起作用。表示被 CORS 政策阻止
被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头。如果不透明的响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用 CORS 的资源。 “img. 的 FetchEvent【参考方案5】:
在我的例子中,我正在使用类似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)
将视频标签设置为crossorigin="anonymous"
【讨论】:
【参考方案6】:我使用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();
);
【讨论】:
【参考方案7】:似乎您正在使用来自未设置正确 Access-Control-Allow-Origin 标头的 URL 的图像,因此存在问题。您可以从服务器获取该图像并从服务器获取它以避免 CORS 问题..
【讨论】:
您能否更准确地回答您的答案,因为我并不真正了解所有这些概念。如何从我的服务器获取该图像? 您从哪里获取该图像,是您的服务器还是其他服务器? 它是这样的:loadImage("example.jpg", 0, 0, 500, 300);我可以把随机的图片url或者图片放在我电脑的同一个文件夹里,还是一样的 是的,但我只是在油漆上创建了图像,它仍然是一样的 我为文件提供了 Access-Control-Allow-Origin:*,但它仍然显示错误【参考方案8】:查看来自 MDN 的 CORS enabled image。 基本上,您必须有一个服务器托管具有适当的 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;
【讨论】:
虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review 感谢您的通知。我只是从 MDN 链接添加内容:)【参考方案9】:这个在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();
);
【讨论】:
【参考方案10】:就像基于@markE 的答案一样。您可以通过本地服务器为您的网站提供服务。在本地服务器上不会出现此错误。
如果您的计算机上安装了 PHP(某些较旧的 MacOS 版本已预安装):
-
打开你的终端/cmd
导航到您的网站文件所在的文件夹
在此文件夹中,运行命令
php -S localhost:3000
打开浏览器并在 URL 栏中转到 localhost:3000。您的网站应该在那里运行。
或
如果您的计算机上安装了 Node.js:
-
打开你的终端/cmd
导航到您的网站文件所在的文件夹
在此文件夹中,运行命令
npm init -y
在 Mac 上运行 npm install live-server -g
或 sudo npm install live-server -g
运行live-server
,它会自动在浏览器中打开一个新标签页,同时打开您的网站。
注意:记得在文件夹的根目录下有一个 index.html 文件,否则您可能会遇到一些问题。
【讨论】:
Mac OS 没有预装 php。我有 macos,它与 PHP 无关。如果你有 php,你或其他人安装了它。 这不是真的@LukasLiesis。一个简单的谷歌搜索会告诉你它是:google.com/…。我曾经使用过的所有 Mac 都预装了它。如果您默认安装了 Big Sur(不是作为更新),您可能需要激活它(尽管我没有这样做):google.com/…。 在终端可以运行php --version
查看你安装的版本。
去获取新的 mac,运行文章中引用的 apache 守护程序脚本,你会发现 PHP 不是 mac 的一部分。除非您运行一些旧版本的操作系统,否则这绝不是一个好主意,至少在 mac 上是这样。关于 Apache 的 php 模块的行甚至不在 httpd.conf
中,但似乎它在旧操作系统上存在。有一行评论:#PHP was deprecated in macOS 11 and removed from macOS 12
你的两篇链接文章都是错误的。 php --version
说command not found
,而 php 不是 mac 的一部分 :)
事实上,我有一个一个月前购买的全新 Macbook,它仍然预装并激活了 PHP。 PHP 立即工作。我唯一能注意到的是我选择了英特尔芯片而不是 M1 芯片(为了与我正在使用的一些软件兼容)。没有人说它是mac的一部分。我说它是预装的,它确实如此 :) 虽然当你在终端中检查 PHP 的版本时,它确实会打印一条警告信息——我引用:“macOS 的未来版本将不包括 PHP。”这是截图:imgur.com/a/ZZL1SwN【参考方案11】:
我还通过在我的代码中添加useCORS : true,
解决了这个错误,例如 -
html2canvas($("#chart-section")[0],
useCORS : true,
allowTaint : true,
scale : 0.98,
dpi : 500,
width: 1400, height: 900
).then();
【讨论】:
【参考方案12】:在我的情况下,我是从我的桌面测试它,即使在将图像本地保存到子文件夹之后也会出现 CORS 错误。
解决方案:
在我的例子中,将文件夹移动到本地服务器 WAMP。从本地服务器完美运行。
注意: 仅当您在本地保存图像时才有效。
【讨论】:
以上是关于受污染的画布不得出口的主要内容,如果未能解决你的问题,请参考以下文章
从画布获取 img (toDataURL) --> 可能无法导出受污染的画布
Amazon S3 图像,无法使用 html5 画布保存,出现受污染的画布错误
GitHubApache 等平台开源项目,受美国出口管制么?