跳转到内容
搜索文档

通过 fetch 转换

最后更新 查看 MarkdownAgent 设置

Workers 让您使用自定义 URL 方案优化图像。

您可以在 Worker 中的 fetch() 子请求上使用 cf.image 属性设置图像优化参数。这与 URL 接口 使用相同的底层机制,但让您对每个图像请求进行程序化控制。

要直接使用图像字节而非 URL,请使用 Images 绑定

以下是 Workers 提供的灵活性的一些示例:

  • 使用自定义 URL 方案。不要在图像 URL 中指定像素尺寸,而是使用 thumbnaillarge 等预设名称。
  • 隐藏原始图像的实际位置。您可以将图像存储在外部 S3 存储桶或服务器的隐藏文件夹中,而无需在 URL 中暴露该信息。
  • 实现内容协商。这对于根据设备和网络状况动态调整图像大小、格式和质量很有用。

工作原理

调整大小功能通过 Worker 内 fetch() 子请求选项 访问。fetch() 函数在第二个参数的 {cf: {image: {…}}} 对象中接受参数。

在 Worker 中,您会使用 fetch(request) 获取图像,添加如下示例中的选项:

fetch(imageURL, {
	cf: {
		image: {
			fit: "scale-down",
			width: 800,
			height: 600,
		},
	},
});

这些类型定义也在我们的 Workers TypeScript 定义库中提供。

cf.image 在托管 Worker 的任何 zone 上可用,包括 *.workers.dev 子域名。每次转换都计费给拥有 Worker 的账户。

配置 Worker

在 Cloudflare 仪表板的 Workers 部分创建新脚本。将 Worker 脚本作用域设置为专门提供资源的路径,例如 /images/*/assets/*。只能调整受支持的图像格式的大小。尝试调整任何其他类型的资源(CSS、HTML)将导致错误。

最好将 Worker 处理的路径与原始(未调整大小)图像的路径分开,以避免图像调整大小 worker 调用自身造成的请求循环。例如,将图像存储在 example.com/originals/ 目录中,通过从 /originals/ 目录获取图像的 example.com/thumbnails/* 路径处理调整大小。如果源图像存储在 Worker 处理的位置,您必须防止 Worker 创建无限循环。

防止请求循环

要执行调整大小和优化,Worker 必须能够从源站服务器获取原始、未调整大小的图像。如果 Worker 处理的路径与服务器上存储图像的路径重叠,Worker 尝试从自身获取图像可能会导致无限循环。

您必须检测哪些请求必须直接转到源站服务器。当 Via 标头中存在 image-resizing 字符串时,表示这是来自另一个 Worker 的请求,应定向到源站服务器:

export default {
	async fetch(request) {
		// If this request is coming from image resizing worker,
		// avoid causing an infinite loop by resizing it again:
		if (/image-resizing/.test(request.headers.get("via"))) {
			return fetch(request);
		}

		// Now you can safely use image resizing here
	},
};

仪表板中缺少预览

Worker 编辑器的脚本预览忽略 fetch() 选项,将始终获取未调整大小的图像。要查看图像转换的效果,您必须部署 Worker 脚本并在编辑器外使用它。

本地开发

运行 wrangler dev 时,使用低保真 mock 在本地应用 cf.image 转换。

支持部分选项,包括 resize、rotate、format 和 background colour。不支持的选项将被忽略。

错误处理

当无法调整图像大小时——例如,因为图像不存在或调整大小参数无效——响应将具有指示错误的 HTTP 状态(例如 400404502)。

默认情况下,错误将转发给浏览器,但您可以决定如何处理错误。例如,您可以将浏览器重定向到原始、未调整大小的图像:

const response = await fetch(imageURL, options);

if (response.ok || response.redirected) {
	// fetch() may respond with status 304
	return response;
} else {
	return Response.redirect(imageURL, 307);
}

请记住,如果服务器上的原始图像非常大,不显示失败的图像可能比回退到使用过多带宽、内存或破坏页面布局的过大图像更好。

您还可以用占位符图像替换失败的图像:

const response = await fetch(imageURL, options);
if (response.ok || response.redirected) {
	return response;
} else {
	// Change to a URL on your server
	return fetch("https://img.example.com/blank-placeholder.png");
}

示例 worker

假设您在 https://example.com/image-resizing设置了 Worker 来处理 https://example.com/image-resizing?width=80&image=https://example.com/uploads/avatar1.jpg 等 URL:

/**
 * Fetch and log a request
 * @param {Request} request
 */
export default {
	async fetch(request) {
		// Parse request URL to get access to query string
		let url = new URL(request.url);

		// Cloudflare-specific options are in the cf object.
		let options = { cf: { image: {} } };

		// Copy parameters from query string to request options.
		// You can implement various different parameters here.
		if (url.searchParams.has("fit"))
			options.cf.image.fit = url.searchParams.get("fit");
		if (url.searchParams.has("width"))
			options.cf.image.width = parseInt(url.searchParams.get("width"), 10);
		if (url.searchParams.has("height"))
			options.cf.image.height = parseInt(url.searchParams.get("height"), 10);
		if (url.searchParams.has("quality"))
			options.cf.image.quality = parseInt(url.searchParams.get("quality"), 10);

		// Your Worker is responsible for automatic format negotiation. Check the Accept header.
		const accept = request.headers.get("Accept");
		if (/image\/avif/.test(accept)) {
			options.cf.image.format = "avif";
		} else if (/image\/webp/.test(accept)) {
			options.cf.image.format = "webp";
		}

		// Get URL of the original (full size) image to resize.
		// You could adjust the URL here, e.g., prefix it with a fixed address of your server,
		// so that user-visible URLs are shorter and cleaner.
		const imageURL = url.searchParams.get("image");
		if (!imageURL)
			return new Response('Missing "image" value', { status: 400 });

		try {
			// TODO: Customize validation logic
			const { hostname, pathname } = new URL(imageURL);

			// Optionally, only allow URLs with JPEG, PNG, GIF, or WebP file extensions
			// @see https://developers.cloudflare.com/images/url-format#supported-formats-and-limitations
			if (!/\.(jpe?g|png|gif|webp)$/i.test(pathname)) {
				return new Response("Disallowed file extension", { status: 400 });
			}

			// Demo: Only accept "example.com" images
			if (hostname !== "example.com") {
				return new Response('Must use "example.com" source images', {
					status: 403,
				});
			}
		} catch (err) {
			return new Response('Invalid "image" value', { status: 400 });
		}

		// Build a request that passes through request headers
		const imageRequest = new Request(imageURL, {
			headers: request.headers,
		});

		// Returning fetch() with resizing options will pass through response with the resized image.
		return fetch(imageRequest, options);
	},
};

测试图像调整大小时,请先部署脚本。仪表板中的在线编辑器不会激活调整大小。

关于 cacheKey 的警告

调整大小的图像始终被缓存。它们作为 fetch 子请求中全尺寸源图像 URL 的缓存条目下的额外变体缓存。不要担心使用许多不同的 Workers 或许多外部 URL——它们不会影响调整大小图像的缓存,您无需为调整大小的图像正确缓存做任何操作。

如果您使用 cacheKey fetch 选项统一多个源 URL 的缓存,不要在 cacheKey 中包含任何调整大小选项。这样做会分散缓存并损害缓存性能。cacheKey 应仅引用全尺寸源图像 URL,而不是其任何调整大小的版本。

这篇文档对您有帮助吗?