橙宝书
端到端项目

项目七 PDF 处理工具

区分 HTML 转 PDF、上传后异步处理和重型原生任务,建立 R2、Queue 与状态接口。

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

PROJECT 07中级约 60 分钟产出:可恢复的 PDF 任务流程

完成标准

HTML 或 URL 可以生成 PDF;上传文件进入 R2 后创建异步任务;用户可查询 queued、running、done、failed;重复消息不会生成冲突结果;大文件、加密文件、超时和处理器失败都有清晰边界。

前置条件与运行随书示例

随书示例先实现一个真实且边界清晰的 html-to-pdf 操作。单元测试完全替换 Browser Run binding,不需要账号也不消耗远程额度:

node --test examples/pdf-tool/test/index.test.mjs

应看到 9 个测试通过,覆盖租户状态查询、Queue 最小消息、危险 HTML 拒绝、D1 失败补偿、PDF 签名、确定性输出 key、重复消费、重试与最终失败。完整 Worker 还需要 D1、R2、主 Queue、dead-letter Queue 与 Browser Run;browser.remote 已按当前 Quick Actions 要求设为 true,因此本地启动完整流程也需要 Cloudflare 登录并消耗 Browser Run 用量。

cp examples/pdf-tool/.dev.vars.example examples/pdf-tool/.dev.vars
pnpm wrangler d1 migrations apply orange-book-pdf-jobs --local --config examples/pdf-tool/wrangler.jsonc
pnpm wrangler dev --config examples/pdf-tool/wrangler.jsonc

处理能力边界

示例把安全 HTML 转成 PDF;它没有声称能合并、拆分、OCR 或解析任意上传 PDF。这些任务必须接入经过独立限制与审计的 Container 或外部处理器。

先把 PDF 任务分三类

任务Cloudflare 起点请求模型
HTML/URL 生成发票、报告、证书Browser Run /pdf可短请求触发,结果仍建议存 R2
上传后摘要、索引、提取R2 event notification + Queue + Worker异步
合并、拆分、OCR、复杂原生库、大文件评估 Container 或外部处理器异步、带资源限制

Browser Run 的 /pdf 接受 urlhtml。它适合把可控页面渲染为文档,不等于通用 PDF 编辑器。Cloudflare 官方的上传后摘要教程使用 R2 event notifications、Queues 和 Workers AI,说明文件处理应该与用户上传请求解耦。

任务状态模型

D1 保存 job ID、tenant、input key、operation、status、attempt、output key 与错误类别;R2 保存输入和输出对象;Queue 传递最小消息。消息只包含稳定 ID 和受控 object key,不包含完整文件或 Secret。

await env.PDF_JOBS.send({
  jobId,
  tenantId,
  inputKey,
  operation: 'extract-text',
});

return Response.json(
  { jobId, status: 'queued' },
  { status: 202 },
);

消费者从可信 job 记录重新加载参数,并使用唯一 output key。若同一消息重复投递,消费者检查 job/version,已完成任务直接确认,不覆盖不同版本结果。

完成处理流程

创建上传合同

认证用户,限制声明大小、允许 application/pdf,生成租户范围内 object key 和 job ID。不要让浏览器决定 bucket 或任意目标路径。

上传后再验证

对象到达 R2 后读取 metadata,检查真实大小、类型、所有权和 job 对应关系。加密、损坏或超出本流程能力的 PDF 进入明确失败状态。

分派到正确处理器

HTML 生成走 Browser Run;文本提取/摘要按当前 Worker 与模型限制评估;依赖原生二进制、耗时长或内存大的工作进入 Container/外部处理器。不要靠分块掩盖一个不适合 Worker 的运行时需求。

保存可恢复结果

先把输出写入版本化 R2 key,再以条件更新将 job 标记 done。若数据库更新失败,重试会发现同一输出;孤儿输出由延迟清理任务处理。

提供状态与下载

状态接口只返回当前用户/租户的任务。下载路由重新授权,设置正确内容类型、文件名与缓存策略,不暴露内部 bucket key。

安全与成本

输入

限制大小、页数或处理时间,拒绝不支持与加密文件。

隔离

object key、job 查询和输出都带可信 tenant 边界。

滥用

上传、生成、状态轮询和下载使用不同限速与业务配额。

隐私

日志不记录文档正文;临时对象有保留期与删除路径。

如果加入摘要或分类,它只是任务处理器中的可选 AI 步骤。PDF 生成、存储、权限与下载在关闭 AI 时仍应工作。

验证矩阵

测试有效小文件、空文件、错误 MIME、超限、加密/损坏、重复消息、处理器超时、R2 写失败、D1 更新失败、跨租户 job ID 和已删除输出。至少一次证明重复消费不会生成两份业务结果。

验证与排障

现象先检查恢复动作
创建任务返回 invalid_or_unsafe_html是否包含脚本、iframe、外部 URL 或超过大小使用自包含、无活动内容的受控 HTML 模板
返回 queue_unavailableproducer binding 与 Queue 是否存在保留失败 job 记录,恢复 Queue 后显式重建任务
job 长时间 runningconsumer、重试次数与 Browser Run 使用量查看 Queue/Browser 指标,避免盲目重复提交
最终 processor_failedBrowser Run 响应、输入对象与配额修复后创建新 version;不要覆盖旧输出 key

远程边界与回滚

Browser Run binding、R2 event notification、Queue consumer、Container 和远程 bucket 都会修改外部状态,本教程不自动创建。代码回滚不能撤回已在队列中的旧消息;消费者要接受版本字段并在不支持时转入 dead-letter/失败状态。删除功能先撤销下载权限和标记过期,再延迟物理删除输入与输出。

下一步:执行生产发布清单

官方来源

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

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

本页目录