Cloudflare Pages + 阿里云 OSS 上传器:一次从故障到稳定的复盘
一个带密码访问控制、支持大文件分片上传的双站点文件上传器实践记录。
目标
这个项目部署在 Cloudflare Pages 上。浏览器只和同源 Worker 通信,OSS 的长期 AccessKey 保留在 Cloudflare Secret 中,不会发送给浏览器。
主要能力:
-
密码登录与安全会话 Cookie
-
国内站点与海外站点切换(港澳台文件上传到海外站点)
-
OSS 分片上传、暂停与继续上传
-
云端文件列表与一键复制链接
-
黑黄主题、中文优先的界面
最终架构
浏览器 │ 同源 API + 会话 Cookie ▼Cloudflare Pages Advanced Mode / Worker │ 在 Worker 内生成 OSS 签名 ▼阿里云 OSS(国内 Bucket / 海外 Bucket)这里最重要的边界是:浏览器绝不持有 OSS 长期密钥;只有 Worker 可以读取 Cloudflare 中的 OSS_ACCESS_KEY_ID 和 OSS_ACCESS_KEY_SECRET。
遇到的主要问题与解决方法
1. 泛化报错掩盖了真实原因
最初页面只提示“上传失败,请检查网络或 Worker 配置”。这类提示对使用者友好,却不利于排错。
改进方式:Worker 返回可理解的 OSS 错误,前端显示后端的具体信息,同时保留简洁的兜底提示。这样才能区分网络问题、鉴权问题和签名问题。
2. SignatureDoesNotMatch 不等于一定是密钥错误
OSS 返回 SignatureDoesNotMatch 的含义是:OSS 服务端计算出的签名,与请求中提供的签名不同。
应按下面顺序排查:
-
确认 Cloudflare Pages Production 环境中的 AccessKey ID 和 Secret 是同一对。
-
确认 Bucket 所在区域与 Endpoint 一致。
-
确认 HTTP 方法、
Date、Content-Type、查询参数与签名字符串完全一致。 -
确认不是误查了独立 Worker:Pages 项目和独立 Worker 是两个资源,变量不会自动共享。
本项目最终确认:文件列表能读取,说明密钥、区域和基础签名已正常;问题只发生在上传对象路径的签名。
3. OSS V1 的“签名路径”和“请求 URL 路径”不能混用
这是本次最关键的修复。
文件名含有中文、空格等字符时:
-
实际发往 OSS 的 URL 路径必须百分号编码;
-
OSS V1 的 CanonicalizedResource 必须使用原始对象名进行签名。
如果将已经编码的路径直接参与 V1 签名,文件列表仍可能正常(列表没有文件对象路径),但初始化分片上传会持续报 SignatureDoesNotMatch。
正确的处理应将两者分开:
const rawPath = `/${objectKey}`; // 用于 V1 签名const requestPath = encodeOssPath(rawPath); // 仅用于实际 fetch URL这个规则对中文文件名、带空格的文件名和特殊字符文件名都很重要。
4. 分片上传的签名查询参数要精确
OSS V1 并不是把所有 URL 查询参数都加入 CanonicalizedResource。
-
列表请求中的
list-type、prefix、max-keys只存在于请求 URL 中; -
分片上传中的
uploads、uploadId、partNumber属于需要签名的 OSS 子资源。
将普通列表参数错误地加入签名,或漏掉分片子资源,都会造成签名不匹配。
5. 外观更新也要保护核心逻辑
一次主题改造中,如果直接用另一份完整 HTML 替换页面,可能意外带回旧的登录或上传脚本。
更稳妥的做法:
-
以已验证版本的登录、会话和上传脚本为基线;
-
只替换 CSS、可见文案和非功能性 HTML;
-
发布后检查浏览器控制台、登录状态、站点切换、文件列表和真实上传。
发布前检查清单
-
GitHub 主分支包含预期提交,Cloudflare Pages 自动部署完成。
-
Pages 的 Production 环境包含
SITE_PASSWORD、OSS_ACCESS_KEY_ID、OSS_ACCESS_KEY_SECRET。 -
两个 OSS Bucket 的区域、Endpoint 和自定义域名正确。
-
登录、文件列表、国内/海外站点切换正常。
-
使用含中文或空格文件名的小文件完成一次真实上传。
-
文件库只显示最近 30 个,并能成功复制链接。
-
不在代码库、截图、日志或公开文档中写入任何密码和 AccessKey Secret。
可复用的经验
-
先获得真实服务端错误,再开始定位。
-
对对象存储签名,逐字符核对“实际请求”和“参与签名的数据”。
-
外观改造与鉴权、上传改造分开提交、分开验证。
-
生产验证必须包含一次真实上传;本地签名单元测试不能完全代替线上验证。
-
凭据永远放在 Secret 中,公开分享只记录变量名和配置原则。
结语
稳定的上传器并不只取决于界面或一次成功的 API 调用。清楚的系统边界、可读的错误信息、对签名细节的严格处理,以及每次发布后的真实验证,才是让它长期可维护的关键。