橙宝书
端到端项目

项目六 图片转换工具

用 R2 管原图、Cloudflare Images 管受支持转换,并建立参数、来源、成本和缓存边界。

编辑与核验:橙宝书编辑团队 ·

PROJECT 06中级约 50 分钟产出:可验证的图片上传与转换流程

完成标准

用户上传受支持图片后能获得稳定对象 key;转换只接受白名单尺寸和格式;Worker 不能被用作任意 URL 代理;重复转换命中缓存;超限、非图片、源对象不存在和转换失败都有明确结果。

前置条件与运行随书示例

纯单元测试不需要 Cloudflare 账号:

node --test examples/image-transformer/test/index.test.mjs

应看到 6 个测试通过,覆盖文件签名、服务端 object key、preset 映射、任意 URL 拒绝、递归保护和下游 404。真实转换还需要一个 R2 bucket、由你控制的 HTTPS 原图域名、已启用的 Images Transformations,以及 allowed origins 中的同一域名。替换 wrangler.jsoncSOURCE_ORIGIN 后再启动:

cp examples/image-transformer/.dev.vars.example examples/image-transformer/.dev.vars
pnpm wrangler dev --config examples/image-transformer/wrangler.jsonc

本地预览与计费边界

wrangler dev 只提供低保真转换模拟。真实 Images 转换需要账号配置,每个唯一源图与参数组合可能产生计费用量;不要用自动化测试批量请求真实转换。

两条图片路径

Cloudflare Images transformations 可以通过格式化 URL 或 Worker fetch()cf.image 参数优化远程图片,并缓存转换结果。需要直接传入图片字节时,使用 Images binding。R2 负责保存你拥有的原图或生成结果,两者职责不要混为一谈。

任务推荐边界
按 URL 缩放、裁剪、质量/格式优化Images transformations
读取或写入原始对象R2 binding
校验用户参数与鉴权Worker
大批量、非即时转换Queue + 后台处理
不受支持的原生编解码评估 Container 或外部处理器

安全的转换路由

不要把用户提供的任意 url 直接交给 fetch(),否则工具可能成为 SSRF 或流量代理。服务端从受控 object key 构造已允许的源域名,只接受预定义宽度和 fit。

const allowedWidths = new Set([320, 640, 1280]);
const width = Number(new URL(request.url).searchParams.get('width') ?? 640);

if (!allowedWidths.has(width)) {
  return Response.json({ error: 'invalid_width' }, { status: 400 });
}

const source = new URL(`/uploads/${safeObjectKey}`, 'https://media.example.com');
return fetch(source, {
  cf: { image: { width, fit: 'scale-down' } },
});

safeObjectKey 必须由认证、所有权与路径规范化产生;示例域名需要替换为你控制且已配置转换的来源。cf.image 的可用参数以当前官方文档和本地类型为准。

上传与转换流程

接收上传意图

先验证用户、套餐、文件声明大小和允许 MIME。生成服务端 object key 与短期上传合同,避免用户控制 bucket 路径或覆盖他人对象。

验证真实对象

上传完成后读取对象 metadata,核对大小、内容类型和所有权。公开列表只返回当前租户可见对象,不把原始内部 key 当作授权。

转换白名单

thumbcardhero 等 preset 映射为服务端尺寸与 fit,不开放任意数值组合。输出 URL 包含稳定 preset/version,便于缓存和未来变更。

处理失败

源对象不存在返回 404;参数非法返回 400;未认证或越权分别返回稳定状态;转换下游失败返回可重试错误,不回显内部 URL 或堆栈。

观察成本与滥用

按用户/租户记录上传字节、对象数与转换请求。Images 转换计费归 Worker 所属账户,公开转换端点要做路径级限速和应用配额。

阅读体验

前后对比

显示相同内容的原图与结果,标出真实尺寸、格式和文件大小。

Preset 卡片

用户选择用途,不需要理解所有底层参数。

任务状态

批量转换显示 queued、running、done、failed,并可重试失败项。

无障碍

结果图保留可编辑 alt,装饰图明确空 alt,不让 AI 自动生成错误描述后直接发布。

验证与排障

现象先检查恢复动作
上传返回 415MIME 与真实文件签名是否一致重新导出 JPEG/PNG/WebP/AVIF,不只改扩展名
转换返回 invalid_source_originSOURCE_ORIGIN 是否是无路径、凭据、查询的 HTTPS origin修正服务端配置并重启 Worker
转换返回 source_not_found受控 origin 能否读取返回的 object key检查 R2 自定义域和对象状态
返回 image_transform_loop转换路由是否也拦截原图路径分离 /images/ 与原图 origin,保留 Via 防护

远程边界与回滚

启用 Images transformations、创建 R2 bucket、配置自定义域名和部署限速规则都会改变远程状态。代码回滚不会恢复被覆盖或删除的原图。对象 key 使用不可变版本,替换图片创建新 key;删除先标记并延迟清理。若新 preset 有问题,回滚路由映射并保留旧 preset URL,避免已发布页面集体破图。

下一步:建立网站与应用防御基线

官方来源

这篇内容帮你完成目标了吗?

内测反馈只在当前浏览器生成,不会自动上传。

本页目录