"Tainted canvases may not be loaded" WebGL 纹理的跨域问题
"Tainted canvases may not be loaded" Cross domain issue with WebGL textures
在过去的 48 小时里,我学到了很多关于跨域策略的知识,但显然还不够。
从 问题开始。我的 HTML5 游戏支持 Facebook 登录。我正在尝试下载人们朋友的个人资料照片。在 HTML5 版本的游戏中,我在 Chrome.
中收到以下错误
detailMessage: "com.google.gwt.core.client.JavaScriptException:
(SecurityError) ↵ stack: Error: Failed to execute 'texImage2D' on
'WebGLRenderingContext': Tainted canvases may not be loaded.
据我了解,出现此错误是因为我试图从不同的域加载图像,但这可以通过 Access-Control-Allow-Origin 解决header,详见 this 问题。
我要下载的URL是
https://graph.facebook.com/1387819034852828/picture?width=150&height=150
查看 Chrome 中的网络选项卡,我可以看到它具有所需的 access-control-allow-origin header 并以 302 重定向响应一个新的 URL。 URL 会有所不同,我猜这取决于负载平衡,但这里有一个例子 URL。
这个URL还有access-control-allow-originheader。所以我不明白为什么会失败。
作为 Facebook,以及成千上万的应用程序、游戏和网站显示用户个人资料图片这一事实,我认为这是可能的。我知道我可以通过我自己的服务器跳转,但我不确定为什么我必须这样做。
回答
我最终使用以下代码在 libgdx 中实现了跨域图像加载(这很老套,我相信可以改进)。我还没有设法让它与 AssetDownloader 一起工作。我希望最终能解决这个问题。
public void downloadPixmap(final String url, final DownloadPixmapResponse response) {
final RootPanel root = RootPanel.get("embed-html");
final Image img = new Image(url);
img.getElement().setAttribute("crossOrigin", "anonymous");
img.addLoadHandler(new LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
HtmlLauncher.application.getPreloader().images.put(url, ImageElement.as(img.getElement()));
response.downloadComplete(new Pixmap(Gdx.files.internal(url)));
root.remove(img);
}
});
root.add(img);
}
interface DownloadPixmapResponse {
void downloadComplete(Pixmap pixmap);
void downloadFailed(Throwable e);
}
您是否在请求 img 之前设置了 crossOrigin
属性?
var img = new Image();
img.crossOrigin = "anonymous";
img.src = "https://graph.facebook.com/1387819034852828/picture?width=150&height=150";
当有人问这个问题时,它正在为我工作。不幸的是,上面的 URL 不再指向任何东西,所以我在下面的示例中更改了它
var img = new Image();
img.crossOrigin = "anonymous"; // COMMENT OUT TO SEE IT FAIL
img.onload = uploadTex;
img.src = "https://i.imgur.com/ZKMnXce.png";
function uploadTex() {
var gl = document.createElement("canvas").getContext("webgl");
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
try {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
log("DONE: ", gl.getError());
} catch (e) {
log("FAILED to use image because of security:", e);
}
}
function log() {
var div = document.createElement("div");
div.innerHTML = Array.prototype.join.call(arguments, " ");
document.body.appendChild(div);
}
<body></body>
如何检查您是否收到 headers
打开您的开发工具,选择网络选项卡,重新加载页面,select 有问题的图像,同时查看请求 header 和响应 header。
该请求应该显示您的浏览器发送了一个 Origin:
header
回复应该显示你收到了
Access-Control-Allow-Methods: GET, OPTIONS, ...
Access-Control-Allow-Origin: *
请注意,响应 和请求 都必须显示以上条目。如果请求丢失 Origin:
那么你没有设置 img.crossOrigin
并且浏览器不会让你使用图像,即使响应说没问题。
如果您的请求有 Origin:
header 而响应没有其他 header 则服务器未授予使用该图像显示它的权限。换句话说,它可以在图像标签中工作,你可以将它绘制到 canvas 但你不能在 WebGL 中使用它,你绘制它的任何 2d canvas 都会被污染并且 toDataURL
和 getImageData
将停止工作
您可以对纹理进行 base64 编码。
这是一个典型的跨域问题,在您进行本地开发时会发生。
我使用 python 的简单服务器作为对此的快速修复。
在终端中导航到您的目录,然后键入:
$ python -m SimpleHTTPServer
你会得到
Serving HTTP on 0.0.0.0 port 8000 ...
所以转到 0.0.0.0:8000/,您应该会看到问题已解决。
在过去的 48 小时里,我学到了很多关于跨域策略的知识,但显然还不够。
从
detailMessage: "com.google.gwt.core.client.JavaScriptException: (SecurityError) ↵ stack: Error: Failed to execute 'texImage2D' on 'WebGLRenderingContext': Tainted canvases may not be loaded.
据我了解,出现此错误是因为我试图从不同的域加载图像,但这可以通过 Access-Control-Allow-Origin 解决header,详见 this 问题。
我要下载的URL是
https://graph.facebook.com/1387819034852828/picture?width=150&height=150
查看 Chrome 中的网络选项卡,我可以看到它具有所需的 access-control-allow-origin header 并以 302 重定向响应一个新的 URL。 URL 会有所不同,我猜这取决于负载平衡,但这里有一个例子 URL。
这个URL还有access-control-allow-originheader。所以我不明白为什么会失败。
作为 Facebook,以及成千上万的应用程序、游戏和网站显示用户个人资料图片这一事实,我认为这是可能的。我知道我可以通过我自己的服务器跳转,但我不确定为什么我必须这样做。
回答
我最终使用以下代码在 libgdx 中实现了跨域图像加载(这很老套,我相信可以改进)。我还没有设法让它与 AssetDownloader 一起工作。我希望最终能解决这个问题。
public void downloadPixmap(final String url, final DownloadPixmapResponse response) {
final RootPanel root = RootPanel.get("embed-html");
final Image img = new Image(url);
img.getElement().setAttribute("crossOrigin", "anonymous");
img.addLoadHandler(new LoadHandler() {
@Override
public void onLoad(LoadEvent event) {
HtmlLauncher.application.getPreloader().images.put(url, ImageElement.as(img.getElement()));
response.downloadComplete(new Pixmap(Gdx.files.internal(url)));
root.remove(img);
}
});
root.add(img);
}
interface DownloadPixmapResponse {
void downloadComplete(Pixmap pixmap);
void downloadFailed(Throwable e);
}
您是否在请求 img 之前设置了 crossOrigin
属性?
var img = new Image();
img.crossOrigin = "anonymous";
img.src = "https://graph.facebook.com/1387819034852828/picture?width=150&height=150";
当有人问这个问题时,它正在为我工作。不幸的是,上面的 URL 不再指向任何东西,所以我在下面的示例中更改了它
var img = new Image();
img.crossOrigin = "anonymous"; // COMMENT OUT TO SEE IT FAIL
img.onload = uploadTex;
img.src = "https://i.imgur.com/ZKMnXce.png";
function uploadTex() {
var gl = document.createElement("canvas").getContext("webgl");
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
try {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
log("DONE: ", gl.getError());
} catch (e) {
log("FAILED to use image because of security:", e);
}
}
function log() {
var div = document.createElement("div");
div.innerHTML = Array.prototype.join.call(arguments, " ");
document.body.appendChild(div);
}
<body></body>
如何检查您是否收到 headers
打开您的开发工具,选择网络选项卡,重新加载页面,select 有问题的图像,同时查看请求 header 和响应 header。
该请求应该显示您的浏览器发送了一个 Origin:
header
回复应该显示你收到了
Access-Control-Allow-Methods: GET, OPTIONS, ...
Access-Control-Allow-Origin: *
请注意,响应 和请求 都必须显示以上条目。如果请求丢失 Origin:
那么你没有设置 img.crossOrigin
并且浏览器不会让你使用图像,即使响应说没问题。
如果您的请求有 Origin:
header 而响应没有其他 header 则服务器未授予使用该图像显示它的权限。换句话说,它可以在图像标签中工作,你可以将它绘制到 canvas 但你不能在 WebGL 中使用它,你绘制它的任何 2d canvas 都会被污染并且 toDataURL
和 getImageData
将停止工作
您可以对纹理进行 base64 编码。
这是一个典型的跨域问题,在您进行本地开发时会发生。
我使用 python 的简单服务器作为对此的快速修复。
在终端中导航到您的目录,然后键入:
$ python -m SimpleHTTPServer
你会得到
Serving HTTP on 0.0.0.0 port 8000 ...
所以转到 0.0.0.0:8000/,您应该会看到问题已解决。