1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
---
title: 启用了 CORS 的图片
slug: Web/HTML/CORS_enabled_image
tags:
- CORS
- Canvas
- HTML
- 参考
- 安全
- 高级
translation_of: Web/HTML/CORS_enabled_image
---
<p><span class="seoSummary">HTML 规范中图片有一个 <code>{{ htmlattrxref("crossorigin", "img") }}</code> 属性,结合合适的 <code>{{Glossary("CORS")}}</code> 响应头,就可以实现在画布中使用跨域 <code>{{ HTMLElement("img") }}</code> 元素的图像,就像在原生 <code>{{ HTMLElement("canvas") }}</code> 中使用一样。</span></p>
<p>查看 <a href="/zh-CN/docs/Web/HTML/CORS_settings_attributes" title="CORS settings attributes">CORS settings attributes</a> 来了解更多 <code>crossorigin</code> 属性的用法。</p>
<h2 id="安全性和“被污染”的_canvas">安全性和“被污染”的 canvas</h2>
<p>由于在 <code>{{HTMLElement("canvas")}}</code> 位图中的像素可能来自多种来源,包括从其他主机检索的图像或视频,因此不可避免的会出现安全问题。</p>
<p>尽管不通过 CORS 就可以在 <code>{{HTMLElement("canvas")}}</code> 中使用其他来源的图片,但是这会<strong>污染</strong>画布,并且不再认为是安全的画布,这将可能在 <code>{{HTMLElement("canvas")}}</code> 检索数据过程中引发异常。</p>
<p>如果从外部引入的 HTML <code>{{HTMLElement("img")}} </code>或 SVG <code>{{SVGElement("svg")}}</code> ,并且图像源不符合规则,将会被阻止从 <code>{{HTMLElement("canvas")}}</code> 中读取数据。</p>
<p>在"被污染"的画布中调用以下方法将会抛出安全错误:</p>
<ul>
<li>在 <code>{{HTMLElement("canvas")}}</code> 的上下文上调用<code>{{domxref("CanvasRenderingContext2D.getImageData", "getImageData()")}}</code></li>
<li>在 <code>{{HTMLElement("canvas")}}</code> 上调用 <code>{{domxref("HTMLCanvasElement.toBlob", "toBlob()")}}</code></li>
<li>在 <code>{{HTMLElement("canvas")}}</code> 上调用 <code>{{domxref("HTMLCanvasElement.toDataURL", "toDataURL()")}}</code></li>
</ul>
<p>这种机制可以避免未经许可拉取远程网站信息而导致的用户隐私泄露。</p>
<h2 id="示例_存储一张外部域中的图片">示例:存储一张外部域中的图片</h2>
<p>在以下例子中,将实现允许检索来自外部的图像,并将其保存在本地存储。要实现这一点,我们需要配置服务器以及网站本身的编码。</p>
<h3 id="网站服务器配置">网站服务器配置</h3>
<p>首先,你必须有一个可以对图片响应正确 <code>Access-Control-Allow-Origin</code> 响应头的服务器。你可以使用以下片段 (来自 <a href="https://github.com/h5bp/server-configs-apache/blob/fc379c45f52a09dd41279dbf4e60ae281110a5b0/src/.htaccess#L36-L53">HTML5 Boilerplate Apache server configs</a>) 实现正确响应头,该配置为允许跨域访问图片。</p>
<pre class="brush:xml"><IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<span class="nt"><FilesMatch</span> <span class="s">"\.(cur|gif|ico|jpe?g|png|svgz?|webp)$"</span><span class="nt">></span>
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule></pre>
<p>现在已经将服务器配置为允许跨源检索这些不同格式的图像。</p>
<h3 id="实现保存功能">实现保存功能</h3>
<p>配置完毕后,你就可以将这些格式的图片保存到本地 <a href="/zh-CN/docs/Web/Guide/API/DOM/Storage" title="/zh-CN/docs/Web/Guide/API/DOM/Storage">DOM 存储</a> 中了,就像这些图片在你自己域名之下一样。</p>
<p>在 <code>{{domxref("HTMLImageElement")}}</code> 上设置 <code>{{domxref("HTMLImageElement.crossOrigin", "crossOrigin")}}</code> 的 <code>{{htmlattrxref("crossorigin")}}</code> 属性,这将允许浏览器在下载图像数据时允许跨域访问请求。</p>
<h4 id="开始下载图片">开始下载图片</h4>
<p>以下代码将在用户点击 "<code>Download</code>" 按钮时开始下载:</p>
<pre><code>function startDownload() {
let imageURL = "https://cdn.glitch.com/4c9ebeb9-8b9a-4adc-ad0a-238d9ae00bb5%2Fmdn_logo-only_color.svg?1535749917189";
downloadedImg = new Image;
downloadedImg.crossOrigin = "Anonymous";
downloadedImg.addEventListener("load", imageReceived, false);
downloadedImg.src = imageURL;
}</code>
</pre>
<p>使用 "硬编码" 图片网址可以很方便的允许图片来自任何地址。当开始下载图片时,我们使用 <code>{{domxref("HTMLImageElement.Image", "Image()")}}</code> 构造器创建新的 <code>{{domxref("HTMLImageElement")}}</code> 对象,将图片的 <code>crossOrigin</code> 属性设置为"<code>匿名</code>"(即,允许对未经过验证的图像进行跨域下载)。添加图片的 <code>{{event("load")}}</code>事件的监听来判断图片数据是否已接收。</p>
<p>最后,将图片的 <code>{{domxref("HTMLImageElement.src", "src")}}</code>属性设置为图片的 URL 以触发图片下载。</p>
<h4 id="接收和保存图片">接收和保存图片</h4>
<p>在 <code>imageReceived()</code> 方法中来处理新下载的图片:</p>
<pre><code>function imageReceived() {
let canvas = document.createElement("canvas");
let context = canvas.getContext("2d");
canvas.width = downloadedImg.width;
canvas.height = downloadedImg.height;
context.drawImage(downloadedImg, 0, 0);
imageBox.appendChild(canvas);
try {
localStorage.setItem("saved-image-example", canvas.toDataURL("image/png"));
}
catch(err) {
console.log("Error: " + err);
}
}</code></pre>
<p>在 <code>HTMLImageElement</code> 调用 <code>imageReceived()</code>来处理"<code>load</code>" 事件以接收下载图片。一旦下载完成全部有效的数据即可触发该事件。从创建新 <code>{{HTMLElement("canvas")}}</code> 元素将图像转换为数据 URL,到访问画布的变量 <code>context</code> 中的 2D 绘图上下文 <code>({{domxref("CanvasRenderingContext2D")}})</code>.</p>
<p>调整画布大小以匹配接收到的图像,然后使用 <code>{{domxref("CanvasRenderingContext2D.drawImage", "drawImage()")}}</code> 将图像绘制到画布中。画布之后被插入到文档中显示。</p>
<p>我们使用 Web Storage API 的本地存储机制来将图片实际存储到本地,该机制可通过全局 <code>{{domxref("Window.localStorage", "localStorage")}}</code> 进行访问。<code>canvas</code> 的 <code>{{domxref("HTMLCanvasElement.toDataURL", "toDataURL()")}}</code> 方法用于将图像转换为 data:// URL 形式的 PNG 格式图片,之后使用 <code>{{domxref("Storage.setItem", "setItem()")}} </code>存储到本地。</p>
<p>您可以在 Glitch 上<a href="https://cors-image-example.glitch.me">尝试</a>或<a href="https://glitch.com/edit/#!/remix/cors-image-example">重写</a>此例子。</p>
<h2 id="另请参阅">另请参阅</h2>
<ul>
<li><a class="external" href="http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html">在 Chrome 13 的 WebGL 中使用跨域图片</a></li>
<li><a class="external" href="http://whatwg.org/html#attr-img-crossorigin">HTML 标准 - <code>crossorigin</code> 属性</a></li>
</ul>
|