允许跨域使用图片和画布
HTML 为图片提供了 crossorigin
属性,该属性与适当的 CORS 标头相结合,允许从外部源加载的 <img>
元素定义的图片在 <canvas>
中使用,就好像它们是从当前源加载的一样。
¥HTML provides a crossorigin
attribute for images that, in combination with an appropriate CORS header, allows images defined by the <img>
element that are loaded from foreign origins to be used in a <canvas>
as if they had been loaded from the current origin.
有关如何使用 crossorigin
属性的详细信息,请参阅 CORS 设置属性。
¥See CORS settings attributes for details on how the crossorigin
attribute is used.
安全和受污染的画布
¥Security and tainted canvases
由于画布位图中的像素可能来自多种来源,包括从其他主机检索的图片或视频,因此不可避免地会出现安全问题。
¥Because the pixels in a canvas's bitmap can come from a variety of sources, including images or videos retrieved from other hosts, it's inevitable that security problems may arise.
一旦你将未经 CORS 批准从其他来源加载的任何数据绘制到画布中,画布就会受到污染。受污染的画布不再被认为是安全的,任何从画布检索图片数据的尝试都会导致抛出异常。
¥As soon as you draw into a canvas any data that was loaded from another origin without CORS approval, the canvas becomes tainted. A tainted canvas is one which is no longer considered secure, and any attempts to retrieve image data back from the canvas will cause an exception to be thrown.
如果外部内容的来源是 HTML <img>
或 SVG <svg>
元素,则不允许尝试检索画布的内容。
¥If the source of the foreign content is an HTML <img>
or SVG <svg>
element, attempting to retrieve the contents of the canvas isn't allowed.
如果外部内容来自从 HTMLCanvasElement
或 ImageBitMap
获取的图片,并且图片源不满足同源规则,则尝试读取画布内容将被阻止。
¥If the foreign content comes from an image obtained from either as HTMLCanvasElement
or ImageBitMap
, and the image source doesn't meet the same origin rules, attempts to read the canvas's contents are blocked.
在受污染的画布上调用以下任何操作都会导致错误:
¥Calling any of the following on a tainted canvas will result in an error:
- 在画布上下文中调用
getImageData()
- 在
<canvas>
元素本身上调用toBlob()
、toDataURL()
或captureStream()
当画布被污染时尝试其中任何一个都会导致抛出 SecurityError
。这可以防止用户在未经许可的情况下使用图片从远程网站提取信息而暴露私有数据。
¥Attempting any of these when the canvas is tainted will cause a SecurityError
to be thrown. This protects users from having private data exposed by using images to pull information from remote websites without permission.
存储来自外国的图片
¥Storing an image from a foreign origin
在此示例中,我们希望允许检索来自外国的图片并将其保存到本地存储。实现这一点需要配置服务器以及为网站本身编写代码。
¥In this example, we wish to permit images from a foreign origin to be retrieved and saved to local storage. Implementing this requires configuring the server as well as writing code for the website itself.
网络服务器配置
¥Web server configuration
我们需要的第一件事是配置为托管图片的服务器,并将 Access-Control-Allow-Origin
标头配置为允许跨域访问图片文件。
¥The first thing we need is a server that's configured to host images with the Access-Control-Allow-Origin
header configured to permit cross-origin access to image files.
假设我们使用 Apache 为我们的网站提供服务。考虑 HTML5 样板 CORS 映像的 Apache 服务器配置文件,如下所示:
¥Let's assume we're serving our site using Apache. Consider the HTML5 Boilerplate Apache server configuration file for CORS images, shown below:
<IfModule mod_setenvif.c>
<IfModule mod_headers.c>
<FilesMatch "\.(avifs?|bmp|cur|gif|ico|jpe?g|jxl|a?png|svgz?|webp)$">
SetEnvIf Origin ":" IS_CORS
Header set Access-Control-Allow-Origin "*" env=IS_CORS
</FilesMatch>
</IfModule>
</IfModule>
简而言之,这将服务器配置为允许从互联网上的任何地方跨域访问图形文件(扩展名为 ".bmp"、".cur"、".gif"、".ico"、".jpg"、".jpeg"、".png"、".svg"、".svgz" 和 ".webp" 的图形文件)。
¥In short, this configures the server to allow graphic files (those with the extensions ".bmp", ".cur", ".gif", ".ico", ".jpg", ".jpeg", ".png", ".svg", ".svgz", and ".webp") to be accessed cross-origin from anywhere on the internet.
实现保存功能
¥Implementing the save feature
现在服务器已配置为允许跨源检索图片,我们可以编写允许用户将它们保存到 本地存储 的代码,就像它们是从运行代码的同一域提供服务一样。
¥Now that the server has been configured to allow retrieval of the images cross-origin, we can write the code that allows the user to save them to local storage, just as if they were being served from the same domain the code is running on.
关键是通过在要加载图片的 HTMLImageElement
上设置 crossOrigin
来使用 crossorigin
属性。这告诉浏览器在下载图片数据时请求跨域访问。
¥The key is to use the crossorigin
attribute by setting crossOrigin
on the HTMLImageElement
into which the image will be loaded. This tells the browser to request cross-origin access when downloading the image data.
开始下载
¥Starting the download
开始下载的代码(例如,当用户单击 "下载" 按钮时)如下所示:
¥The code that starts the download (say, when the user clicks a "Download" button), looks like this:
function startDownload() {
let imageURL =
"https://cdn.glitch.com/4c9ebeb9-8b9a-4adc-ad0a-238d9ae00bb5%2Fmdn_logo-only_color.svg?1535749917189";
let imageDescription = "The Mozilla logo";
downloadedImg = new Image();
downloadedImg.crossOrigin = "anonymous";
downloadedImg.addEventListener("load", imageReceived, false);
downloadedImg.alt = imageDescription;
downloadedImg.src = imageURL;
}
我们在这里使用硬编码 URL (imageURL
) 和关联的描述性文本 (imageDescription
),但这很容易来自任何地方。为了开始下载图片,我们使用 Image()
构造函数创建一个新的 HTMLImageElement
对象。然后将镜像配置为允许跨域下载,将其 crossOrigin
属性设置为 "anonymous"
(即允许镜像跨域免认证下载)。添加了一个事件监听器,用于在图片元素上触发 load
事件,这意味着图片数据已被接收。替代文本被添加到图片中;虽然 <canvas>
不支持 alt
属性,但该值可用于设置 aria-label
或画布的内部内容。
¥We're using a hard-coded URL (imageURL
) and associated descriptive text (imageDescription
) here, but that could easily come from anywhere. To begin downloading the image, we create a new HTMLImageElement
object by using the Image()
constructor. The image is then configured to allow cross-origin downloading by setting its crossOrigin
attribute to "anonymous"
(that is, allow non-authenticated downloading of the image cross-origin). An event listener is added for the load
event being fired on the image element, which means the image data has been received. Alternative text is added to the image; while <canvas>
does not support the alt
attribute, the value can be used to set an aria-label
or the canvas's inner content.
最后将图片的 src
属性设置为要下载的图片的 URL;这会触发下载开始。
¥Finally, the image's src
attribute is set to the URL of the image to download; this triggers the download to begin.
接收并保存图片
¥Receiving and saving the image
处理新下载的图片的代码可以在 imageReceived()
方法中找到:
¥The code that handles the newly-downloaded image is found in the imageReceived()
method:
function imageReceived() {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
canvas.width = downloadedImg.width;
canvas.height = downloadedImg.height;
canvas.innerText = downloadedImg.alt;
context.drawImage(downloadedImg, 0, 0);
imageBox.appendChild(canvas);
try {
localStorage.setItem("saved-image-example", canvas.toDataURL("image/png"));
} catch (err) {
console.error(`Error: ${err}`);
}
}
调用 imageReceived()
来处理接收下载图片的 HTMLImageElement
上的 "load"
事件。一旦下载的数据全部可用,就会触发此事件。首先创建一个新的 <canvas>
元素,我们将使用该元素将图片转换为数据 URL,并通过变量 context
访问画布的 2D 绘图上下文 (CanvasRenderingContext2D
)。
¥imageReceived()
is called to handle the "load"
event on the HTMLImageElement
that receives the downloaded image. This event is triggered once the downloaded data is all available. It begins by creating a new <canvas>
element that we'll use to convert the image into a data URL, and by getting access to the canvas's 2D drawing context (CanvasRenderingContext2D
) in the variable context
.
调整画布的大小以匹配接收到的图片,将内部文本设置为图片描述,然后使用 drawImage()
将图片绘制到画布中。然后将画布插入到文档中,以便图片可见。
¥The canvas's size is adjusted to match the received image, the inner text is set to the image description, then the image is drawn into the canvas using drawImage()
. The canvas is then inserted into the document so the image is visible.
现在是时候在本地实际保存图片了。为此,我们使用 Web Storage API 的本地存储机制,该机制通过 localStorage
全局进行访问。The canvas method toDataURL()
is used to convert the image into a data:// URL representing a PNG image, which is then saved into local storage using setItem()
.
¥Now it's time to actually save the image locally. To do this, we use the Web Storage API's local storage mechanism, which is accessed through the localStorage
global. The canvas method toDataURL()
is used to convert the image into a data:// URL representing a PNG image, which is then saved into local storage using setItem()
.