写作说明:本文由 GPT-6 生成,整理自我与 Codex 的讨论和实际操作,回顾 Andy Live 第一版从相机传图到网页的任务。第一版链路已经跑通;后台审核、客户二维码和收款功能仍待部署。
我最初想做的事情很简单:在现场拍完照,让对方过一会儿就能从网页看到照片。真正动手后,我才发现“拍完就上网”不是一个动作,而是一连串交接。相机要把文件交给手机,手机要交给云存储,网站要知道该展示什么,最后还得想清楚谁能下载、谁能删除。
这篇文章按我和 CodeX 实际解决问题的顺序写。文中的配置截图没有密钥,也没有客人的照片;报错信息保留了排障所需的部分。写到这里时,传图和现场展示已经上线,后台审核、客户二维码与 Stripe 收款的代码已在独立仓库完成本地验证,尚未配置并部署到正式网站。
先把目标缩到一张 2MP 照片
一开始,我也想过高清 USB 抽图、人脸识别、自动修图和在线售卖。但尼康 D5600 到手机这一步,最容易稳定取得的是 SnapBridge 传来的约 2MP 照片。高清原图没有同样省事的自动传输方式。于是第一版先不碰这些能力,只验证:
D5600 → SnapBridge → Android 手机 → FolderSync → Cloudflare R2 →
/live网页
这不是完全无人值守。D5600 到手机有时仍要我手动点一下;手机之后才由 FolderSync 按计划同步。把这点讲清楚很重要,否则网页上没有新照片时,我会把相机、手机、云端和网站混成同一个故障。
手机到 R2:表单填对只是第一关
我先在 Cloudflare 创建了私有存储桶 andy-live。那时桶里还是空的:
FolderSync 的账户类型藏在“文件协议”下的 S3 Compatible,不在上面的云服务列表里。找到它之后,Cloudflare 给我的几项凭据又和手机上的字段名称不完全一致。最后我把对应关系整理成这样:
| FolderSync 字段 | R2 对应内容 |
|---|---|
| Access Key ID | R2 的访问密钥 ID |
| Secret Access Key | R2 的机密访问密钥 |
| 服务器地址 | https://<Account ID>.r2.cloudflarestorage.com |
| 区域 | auto |
令牌值不是 S3 的 Access Key ID;服务器地址也不是存储桶页面的网址。手机账号测试一开始报 Access Denied,随后又明确提示 us-west-2 不是有效区域,列出的可用值包含 auto。改用 auto 并核对令牌权限、地址和存储桶范围后,账户测试终于通过。
然而“账户测试通过”只证明手机能连接 R2,不代表照片已经在同步。我还得建文件夹配对:左侧选手机里 SnapBridge 实际保存照片的目录,右侧选刚才建立的 S3 Compatible 账户与存储桶,方向选从手机到云端。计划任务用五分钟作为最短重复间隔,文件加密先不启用。
接下来又遇到一次 SyncFailedException。真正有用的线索是界面上的“未找到访问文件的权限”,不是那一长串 Java 堆栈。Android 的“照片和视频”权限已经允许,但 FolderSync 仍缺少“管理所有文件”的访问权;补齐后,照片才真正进入 R2。我第一次在对象列表里看到的路径是 previews/_DSC0047.JPG。
之后同步还报过 STREAMING-AWS4-HMAC-SHA256-PAYLOAD not implemented。我调整了 FolderSync 的远端快速检查和“禁用校验和计算”选项,错误停止了。因为两个选项是在同一轮排查中调整的,我不能把结果武断地归因于其中某一个;可以确认的是,这组设置在当时的手机与 R2 组合上完成了上传。
R2 有了对象,网页才开始有意义
照片进桶以后,下一问才是“R2 怎么到我的网站”。我们给 andy-live 做了独立的网页服务,由服务器用只读凭据列出 R2 对象并生成短时访问链接;存储桶本身继续保持私有。现场页按上传时间每 20 分钟分一组,手机同步后,页面会轮询更新。
我当时不想把现场页限制成“只显示最新 50 张”。后来补上审核机制,这个想法有了更准确的边界:可以展示全部已审核且明确设为公开的照片,而不是把桶里每个刚上传的文件都直接放出来。同步频率和页面轮询也会增加 R2 请求数,所以还要看实际操作量,不能只盯着存储容量。
这里有两个容易说过头的地方。第一,网页显示的时间来自 R2 对象的上传或修改时间,并不是相机快门的拍摄时间。第二,页面上写“请及时保存”很容易,真正承诺“24 小时内删除”却必须有实际清理任务与付款后的保留规则。没有做到的期限,不能只靠一行提示文字来保证。
当照片能看见,问题变成“谁可以看、谁可以拿”
看到现场页正常工作后,我开始担心另一件事:所有传上来的照片是不是都该公开?如果客户只买一张,能否阻止他把整批照片下载走?万一某张照片有问题,我能不能在手机上立刻撤下?
这时 andy-live 从“对象列表页面”变成了一个带权限的小系统。我们决定让新上传的照片先处于待审核状态;我在手机后台选图,系统再生成带水印的浏览图,以及独立存放的 2MP 交付文件。公开现场页、客户二维码页与付款后的下载地址各有不同权限:
客户二维码只包含我挑选的照片,可以设置有效期或撤销。付款按照片逐张计算,不能靠客户从 Stripe 付款页面“返回成功”就放行下载;服务器必须收到并核验付款回调,再核对分享链接、照片和订单。后台还能下架或删除照片,查看最近订单状态。现在交付文件仍是 SnapBridge 得到的约 2MP 直出图,不是尚未接入的相机高清原图。
在技术之外,我也反复问过:一张卖 HK$10 是否值得,支付手续费会吃掉多少,小规模街头拍摄又涉及哪些经营与场地规则。这些问题没有因为接入 Stripe 就消失。自动收款只能处理付款状态;定价、经营安排和合规判断仍要在真正营业前单独确认。
改网页,也得知道代码从哪里来
在我们给现场页加时间分组时,我又追问了一个看似琐碎的问题:“这页到底改的是服务器上的哪一份代码?”如果只在服务器里手工改,下一次怎么复现、怎么回滚?
后来我们把 andy-live 拆成独立的私有 GitHub 仓库,给它接上 GitHub Actions。代码先在本地检查,PR 再跑自动测试;合并到主分支后构建镜像,由 Contabo 服务器先启动候选容器,检查页面、健康接口和照片接口,成功才替换正式容器。这样,网站正在运行哪个提交,终于可以核对,而不必凭“我记得当时改过”来判断。
这也解释了为什么现在的第二版还没有直接上线。后台、二维码、逐张收款和可配置清理已经写好,15 项本地测试(含临时 PostgreSQL)、类型检查与构建通过;但正式服务器还缺后台专用 R2 写入凭据、数据库与登录配置,Stripe 商家账户和测试回调也尚未接通。自动清理默认关闭,不能宣称线上已经在定时删照片。
目前跑通了什么
截至 2026 年 10 月 3 日,可以把整个过程分成两段:
| 已经可用 | 仍待配置和联通测试 |
|---|---|
| D5600 经 SnapBridge 到手机;必要时手动确认 | 后台专用 R2 读写凭据与数据库 |
| FolderSync 把约 2MP 照片送进私有 R2 | 管理员登录、审核、二维码的正式环境验证 |
/live 展示照片并按 20 分钟分组 |
Stripe 测试付款、回调、逐张下载与退款流程 |
| 独立仓库的 CI/CD 与服务器候选镜像检查 | 备份恢复演练、清理期限的最终选择与上线 |
回看这些对话,真正推进项目的,不是一次性把所有功能列齐,而是每次只问“文件现在到了哪里”。账户测试通过后要看对象有没有出现;对象出现后要看网页是否能读;网页能读后才谈审核和售卖。每一段都留下可观察的结果,也就知道下一次出问题该从哪一段查起。