这一篇讲两件事:Storage(文件上传/访问)和 Realtime(实时订阅)。
先把口径说清楚:在微信小程序里,Storage 更接近“稳定可用”,Realtime 更接近“按需评估”。原因不是 Supabase 的后端能力不够,而是小程序端的依赖链与运行时限制更苛刻(01 里提到的 phoenix 兼容性问题就是一个典型例子)。所以本文的结构也会偏向“先把 Storage 做稳”,Realtime 放在后半段,并给出替代方案与启用前检查清单。
第一站:展示你的灵感蓝图(Storage 机制解析)
要分享一个极具创意的想法,几张设计草图是必不可少的。Supabase Storage 是基于 AWS S3 协议构建的对象存储服务,它被抽象为三层结构:
- Bucket(存储桶):最高层级的容器。分为 Public(公开,如图床)和 Private(私有,如机密合同)。
- Object(对象):实际存储的文件。
- URL:访问文件的方式。
Bucket 是否 public 取决于你的业务:如果图片是对所有人可见的内容,可以用 public bucket;如果图片属于用户私有资料或订单附件,建议用 private bucket + signed URL。下面示例以 public bucket blueprints 为例。
1. 小程序上传入口:wx.uploadFile
Supabase 官方 SDK 默认用 fetch + FormData 上传文件,小程序逻辑层没有这套 API。使用 supabase-wechat-stable-v2 后,业务层可以继续用 storage.upload() 这套接口,底层会转成 wx.uploadFile。
示例:
// 选择本地相册图片
const res = await wx.chooseMedia({ count: 1 });
const tempFilePath = res.tempFiles[0].tempFilePath;
// 像调用标准 API 一样直接上传
const { data, error } = await supabase.storage
.from('blueprints')
.upload(`sketches/quantum_design.jpg`, tempFilePath, {
upsert: true
});
这里有一个容易忽略的细节:上传字段名必须是 file,否则 Storage 接口会返回格式不符合预期的错误。
2. 路径规范与权限边界
如果所有人都能上传图片到 blueprints Bucket,会不会有人恶意覆盖别人的设计图?
这就是 Storage RLS 发挥威力的地方了。每一个上传的文件,在底层的 storage.objects 表中都有对应的元数据。
推荐的路径命名法是:{user_id}/{timestamp}-{random}.jpg。
配合这个路径规则,可以写一个最小可用的 Storage policy:
-- 用户只能往自己 UUID 命名的文件夹里塞草图
CREATE POLICY "用户只能管理自己的灵感库"
ON storage.objects FOR INSERT
TO authenticated
WITH CHECK (
bucket_id = 'blueprints'
AND (storage.foldername(name))[1] = auth.uid()::text
);
storage.foldername(name) 会把文件路径按 / 切成数组,这里校验第一个元素是不是当前用户的 ID,保证用户只能往自己的目录写。
第二站:实时订阅(Realtime)在小程序的现实边界
如果你的小程序确实需要“数据库一有变化就推给前端”,Realtime 是一个很方便的能力。但在小程序里启用它之前,建议先回答两个问题:
- 你是不是必须要秒级实时?如果只是“评论数刷新 / 列表有新数据提示”,很多场景用轮询 + 增量拉取就够了。
- 你的构建链路是否已经解决 phoenix 兼容性问题?如果你在 01 中用了 phoenix stub,那么 Realtime 相关能力等同于未启用。
1. Realtime 的底层协议与小程序适配
Supabase 的 Realtime 并不是无中生有,它底层采用的是大名鼎鼎的 Phoenix Channel 协议。 传统的 WebSocket 是“一根管子全家共用”,容易发生数据混乱;而 Phoenix Channel 在连接之上引入了**“频道(Channel)”**的概念,实现了多路复用。
小程序的 SocketTask 和标准浏览器 WebSocket API 不同(事件绑定方式、关闭语义等都不一样)。适配包要做的事情,本质是把 SocketTask 包装成 SDK 期望的 WebSocket 形状。
2. 三种订阅模式(先按需求挑)
Supabase Realtime 有三种订阅模式,建议按需求选择:
-
Postgres Changes(监听数据库变更)
- 原理:服务端通过解析 Postgres 的 WAL(Write-Ahead Log),只要
messages表里有一条新记录INSERT,立刻推给客户端。 - 应用场景:接收协作者发来的新消息。讨论记录是必须持久化进数据库的重要资产。
- 原理:服务端通过解析 Postgres 的 WAL(Write-Ahead Log),只要
-
Broadcast(广播)
- 原理:客户端把消息发给服务端,服务端不进数据库,直接广播给房间里的其他人。
- 应用场景:“队友正在输入...” 的顶部提示。这种稍纵即逝的状态,存进数据库毫无意义,直接广播最快。
-
Presence(在线状态)
- 原理:底层采用 CRDT 算法,在分布式网络中同步当前频道有哪些用户,以及他们的附加状态(如昵称、头像)。
- 应用场景:灵感详情页显示“当前有 3 人正在浏览此草案”,或者讨论区顶部显示对方“当前在线/离线”。
在小程序生命周期里,订阅与退订一定要成对出现:进入页面订阅,离开页面退订,否则会积累无效连接与监听,最终表现成内存与连接数异常。