返回博客列表
技术文章Supabase微信小程序实时通信文件存储

实时通信与文件存储

2025年05月🇨🇳 中文

这一篇讲两件事: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 是一个很方便的能力。但在小程序里启用它之前,建议先回答两个问题:

  1. 你是不是必须要秒级实时?如果只是“评论数刷新 / 列表有新数据提示”,很多场景用轮询 + 增量拉取就够了。
  2. 你的构建链路是否已经解决 phoenix 兼容性问题?如果你在 01 中用了 phoenix stub,那么 Realtime 相关能力等同于未启用。

1. Realtime 的底层协议与小程序适配

Supabase 的 Realtime 并不是无中生有,它底层采用的是大名鼎鼎的 Phoenix Channel 协议。 传统的 WebSocket 是“一根管子全家共用”,容易发生数据混乱;而 Phoenix Channel 在连接之上引入了**“频道(Channel)”**的概念,实现了多路复用。

小程序的 SocketTask 和标准浏览器 WebSocket API 不同(事件绑定方式、关闭语义等都不一样)。适配包要做的事情,本质是把 SocketTask 包装成 SDK 期望的 WebSocket 形状。

2. 三种订阅模式(先按需求挑)

Supabase Realtime 有三种订阅模式,建议按需求选择:

  1. Postgres Changes(监听数据库变更)

    • 原理:服务端通过解析 Postgres 的 WAL(Write-Ahead Log),只要 messages 表里有一条新记录 INSERT,立刻推给客户端。
    • 应用场景接收协作者发来的新消息。讨论记录是必须持久化进数据库的重要资产。
  2. Broadcast(广播)

    • 原理:客户端把消息发给服务端,服务端不进数据库,直接广播给房间里的其他人。
    • 应用场景“队友正在输入...” 的顶部提示。这种稍纵即逝的状态,存进数据库毫无意义,直接广播最快。
  3. Presence(在线状态)

    • 原理:底层采用 CRDT 算法,在分布式网络中同步当前频道有哪些用户,以及他们的附加状态(如昵称、头像)。
    • 应用场景灵感详情页显示“当前有 3 人正在浏览此草案”,或者讨论区顶部显示对方“当前在线/离线”。

在小程序生命周期里,订阅与退订一定要成对出现:进入页面订阅,离开页面退订,否则会积累无效连接与监听,最终表现成内存与连接数异常。