Workers 让您使用自定义 URL 方案优化图像。
您可以在 Worker 中的 fetch() 子请求上使用 cf.image 属性设置图像优化参数。这与 URL 接口 使用相同的底层机制,但让您对每个图像请求进行程序化控制。
要直接使用图像字节而非 URL,请使用 Images 绑定。
以下是 Workers 提供的灵活性的一些示例:
- 使用自定义 URL 方案。不要在图像 URL 中指定像素尺寸,而是使用
thumbnail和large等预设名称。 - 隐藏原始图像的实际位置。您可以将图像存储在外部 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 的账户。
在 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 状态(例如 400、404 或 502)。
默认情况下,错误将转发给浏览器,但您可以决定如何处理错误。例如,您可以将浏览器重定向到原始、未调整大小的图像:
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");
}假设您在 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);
},
};测试图像调整大小时,请先部署脚本。仪表板中的在线编辑器不会激活调整大小。
调整大小的图像始终被缓存。它们作为 fetch 子请求中全尺寸源图像 URL 的缓存条目下的额外变体缓存。不要担心使用许多不同的 Workers 或许多外部 URL——它们不会影响调整大小图像的缓存,您无需为调整大小的图像正确缓存做任何操作。
如果您使用 cacheKey fetch 选项统一多个源 URL 的缓存,不要在 cacheKey 中包含任何调整大小选项。这样做会分散缓存并损害缓存性能。cacheKey 应仅引用全尺寸源图像 URL,而不是其任何调整大小的版本。