死在了网络上,不是死在技术上
当博客文件上传功能撞上 Vercel 4.5MB 限制时,我以为技术上做完了。然后网络告诉我:你还是个小学生。
《死在了网络上,不是死在技术上》
折腾了三个云服务、四个方案、五个小时。 最后发现,最大障碍不是 4.5MB 限制,不是代码 bug,不是配置问题。 是 GFW。 是 网络。 是 我无法控制的物理世界。
起 因:4.5MB 的天花板
一切源于一个简单的需求:让博客支持临时上传文件。
一开始,这很简单。Vercel 自带 Blob 存储,配置好 Dashboard 就能用。我兴冲冲地写好了上传 API、文件类型白名单、安全验证。一切看起来都很完美——直到我想测试一个 MP4 文件。
Request Entity Too Large FUNCTION_PAYLOAD_TOO_LARGE
我盯着屏幕上 413 错误,以为是代码 bug。反复检查配置文件、压缩代码、重试——结果还是同样的错误。
后来我才明白:Vercel Serverless Functions 的请求体上限是 4.5MB,这是基础设施级别的限制,不是配置问题。
更要命的是:升级 Pro 计划也不能突破。
我翻遍了 Vercel 官方文档,看到一句话:
"Vercel の Serverless Function のボディサイズ上限は インフラレベルの制限 であり、アプリケーションコードでの設定変更では回避できない"
—— 翻译过来就是:这是基础设施级别的限制,改代码没用。
那一刻,我意识到:这个限制,付费也解决不了。
第一次尝试:迁移到 Cloudflare R2
既然 Vercel 解决不了,那就换平台。
Cloudflare R2 是个不错的选择:
- 兼容 S3 协议
- 10GB 免费存储
- 零 egress 费用(流量免费)
- 全球边缘节点
我注册了 Cloudflare 账户,绑定了付款方式(即使有免费额度也需要),创建了 Bucket。
第一次测试时,我直接用 Cloudflare API 上传了一个 100MB 的文件——成功!
下载速度 1.29 MB/s(因为数据存储在欧盟,从中国访问会跨欧亚大陆)。
R2 本身没问题。但问题又来了:博客的上传接口是 Vercel 函数。即使我把存储换成了 R2,文件仍需先经过 Vercel 函数才能转发到 R2——而 Vercel 函数的 4.5MB 限制依然存在。
用户上传文件 → Vercel 函数(4.5MB 限制) → R2
↑
卡在这里
绕了一圈,问题又回到了原点。
第二次尝试:突破 Vercel 函数的限制
我想到了另一个思路:让浏览器绕过 Vercel 函数,直接上传到 R2。
这需要 Presigned URL:
- 浏览器请求 Vercel API(不带文件,只带文件名)
- Vercel 返回 R2 的预签名 URL
- 浏览器直接 PUT 文件到 R2
- Vercel 不参与文件传输
这听起来很完美。但实现起来需要 R2 的 S3 兼容 Access Key,而 Cloudflare Dashboard 的 Token 申请界面有点混乱——我之前创建的 Token 是 Cloudflare 通用 API Token(cfat_ 开头),不是 R2 S3 格式的。
这条路需要重写后端代码,加上 OAuth 流程——我决定先搁置。
第三次尝试:Cloudflare Worker 直传
再换一个思路:把整个上传功能从 Vercel 搬到 Cloudflare Worker。
Cloudflare Worker 是无服务器计算,但不受 4.5MB 限制(因为它根本不经过 Vercel)。
我去 GitHub 上搜了一圈,找到了一个完美的项目:
R2-Explorer
这是一个开源的 R2 网盘界面(GitHub):
- ✅ Google Drive 风格的 UI
- ✅ 直接部署到 Cloudflare Worker(无大小限制)
- ✅ 多文件上传、文件夹管理
- ✅ 文件预览(图片、PDF、Markdown)
- ✅ Basic Auth 密码保护
- ✅ 多语言支持
- ✅ 活跃维护
最关键的是:它直接跑在 Cloudflare Worker 上,完全绕过了 Vercel。
这正是我要的。
网络冲击:墙外的东西墙内看不到
R2-Explorer 找到了,方案也设计好了。理论上我只要把 Worker 部署上去,国内国外都能用。
但现实是:国内访问 workers.dev 子域被 GFW 精准干扰。
我本机测试:
- ✅ 部署成功,Worker URL 正常返回
- ❌ 浏览器打开
lookword-r2-explorer.openclaw1990.workers.dev—— 加载不出来
不只是 workers.dev,更严重的是:
- Cloudflare R2 的 S3 Endpoint:
*.r2.cloudflarestorage.com同样被墙 - 即便 Worker 跑在边缘节点,国内请求根本到不了
这就是 GFW 的真实威力:
- 国外的朋友测下来完美工作
- 我在国内 ping 都不通
- 部署位置 ≠ 访问位置
- "全球 CDN" 在墙面前就是一句空话
更让人崩溃的是:国内没有对等的免费对象存储。
- 阿里云 OSS、腾讯云 COS 都需要实名认证 + 备案域名
- 七牛云、又拍云免费额度小,且需要 HTTPS 证书
- 没有一个能做到"开箱即用、零配置、零费用"
我第一次真切地感受到:技术方案的成败不取决于代码好坏,取决于网络是否可达。
部署的那一刻我才明白:
"死在了网络上,不是死在技术上" 这不是修辞,是现实。
实施过程
第一步:准备 R2 S3 凭证
R2-Explorer 是基于 S3 协议的客户端,需要 S3 Access Key + Secret Access Key。
我回到 Cloudflare Dashboard 的 R2 API Tokens 页面,重新创建了一个 Token。这次特别留意:R2 API Key(不是 Cloudflare API Token),把完整的 3 个值都复制了下来:
- Cloudflare API Token
- Access Key ID(S3 格式)
- Secret Access Key(S3 格式)
⚠️ 重要教训:Secret Access Key 只在创建时显示一次,必须立即保存到密码管理器。
第二步:安装 R2-Explorer
R2-Explorer 的部署非常简单,只需一个 wrangler 配置文件 + 一行代码的 Worker:
// src/index.ts
import { R2Explorer } from "r2-explorer";
export default R2Explorer({
readonly: false,
basicAuth: {
username: "admin",
password: "<your-password>",
},
});
wrangler.toml 配置 R2 Bucket 绑定:
name = "lookword-r2-explorer"
compatibility_date = "2024-11-06"
main = "src/index.ts"
[[r2_buckets]]
binding = "BUCKET"
bucket_name = "lookwordtroupe"
第三步:部署
我用 wrangler 部署到 Cloudflare Workers。第一次尝试失败,因为我最初创建的 Token 没有 Workers Scripts:Edit 权限。
修复方法很简单:在 Dashboard 编辑 Token,添加 Workers Scripts:Edit 权限。
第二次部署成功!Worker URL 是:
https://lookword-r2-explorer.openclaw1990.workers.dev
第四步:测试
我让一个在国外的朋友用手机 4G 网络测试,结果:
- ✅ 登录页面正常显示
- ✅ 输入用户名密码后进入网盘界面
- ✅ 文件上传/下载/删除功能完全正常
完全工作!
只是在国内网络环境下访问 workers.dev 子域会被防火墙干扰。这是另一个层面的问题,但功能本身是完整的。
当前架构
┌─────────────────────────────────────┐
│ 博客私密空间(Vercel Blob) │
│ - 4MB 限制 │
│ - 国内可访问 │
│ - 集成在博客里 │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│ R2 文件空间(Cloudflare Worker) │
│ - 无大小限制 │
│ - 国外/4G 可访问 │
│ - 独立 URL │
│ - 见下方"访问信息" │
└─────────────────────────────────────┘
两个方案并存:
- 私密空间:日常小文件,国内访问方便,4MB 限制
- R2 文件空间:大文件临时传输,需要 4G 或 VPN
关键经验教训
1. 基础设施限制是付费也解决不了的
Vercel Serverless Functions 的 4.5MB 请求体限制,所有计划都一样(Hobby/Pro/Enterprise)。这不是"钱的问题",是架构问题。
2. 凭证管理要小心
Cloudflare R2 的 Token 申请界面有点混乱:
- Cloudflare API Token(
cfat_开头):管理 R2 配置 - R2 API Key(S3 格式):操作 R2 对象
我一开始用了前者,发现不能用于 S3 客户端。第二次才搞清楚两者的区别。
3. 部署位置不一定等于访问位置
Cloudflare Worker 不是部署在单一服务器位置——它运行在全球 200+ 边缘节点。但 R2 Bucket 是部署在特定区域的(我选了 EU),所以数据访问速度受区域影响。
4. 开源项目的力量
R2-Explorer 是 GitHub 上的开源项目,已经做得非常完善。我只需要写 3 行代码 + 一个 wrangler 配置就能拥有完整的网盘功能。这比从头开发节省了大量时间。
访问信息
凭据请参考我的密码管理器,不要写在这里。
R2 文件空间(Cloudflare Worker):
https://lookword-r2-explorer.openclaw1990.workers.dev
特性:
- ✅ Google Drive 风格界面
- ✅ 拖放上传大文件
- ✅ 文件夹管理
- ✅ 文件预览
- ✅ 密码保护
使用场景:
- 电脑 ↔ 手机 跨平台传输
- 国内 ↔ 国外 跨网络传输
- 大于 4MB 的文件(如视频、压缩包)
- 临时分享链接
注意事项:
- 国内访问可能需要 VPN 或 4G
- 海外或 4G 网络下完全正常
- 密码请从密码管理器获取(不要写在博客里)
- 用完即删,不作长期存储
技术栈总结
| 服务 | 用途 | |------|------| | Vercel | 博客部署 + Serverless API | | Vercel Blob | 小文件存储(4MB) | | Upstash Redis | 元数据存储 | | Cloudflare R2 | 大文件对象存储 | | Cloudflare Worker | R2-Explorer 网盘界面 | | R2-Explorer | 开源 R2 网盘 UI | | Gitee | 代码托管 | | wrangler | Cloudflare 部署工具 |
后记
这次探索让我对"限制"有了三层认知。
第一层:代码的限制
- Vercel Serverless Functions 的 4.5MB 请求体
- AWS Lambda 的 6MB / 256KB 限制
- 这些都是"代码层面"的限制
第二层:架构的限制
- 即使换了 Cloudflare R2,Vercel 函数还在中间
- 必须搬走整个上传流程到 Cloudflare Worker
- 这是"架构层面"的限制
第三层:物理的限制
- Worker 跑在 Cloudflare 全球边缘
- 但国内访问不到
- 这是 网络层面的限制 —— 我无法控制
三种限制层层叠加。前两层靠技术能解决,第三层——只能妥协。
最终的妥协方案是:
| 场景 | 方案 | |------|------| | 国内小文件 | Vercel Blob(4MB 限制) | | 国内大文件 | 没方案(要 VPN) | | 国外小文件 | Vercel Blob | | 国外大文件 | R2-Explorer Worker |
这不是最优解,但是当前最务实的方案。
或许,这就是工程师的浪漫:
在层层限制中,找到一条窄路。
然后接受这条路上还有一堵墙。
然后接受墙那边是你的盲区。
然后继续走。
这不是悲观,是清醒。
写于 2026 年 6 月 21 日 一次跨越三个云服务的探索 用 Markdown + AI 协作的方式记录 有总比没有强,技术债慢慢还 🎉