Bucket 配置
avatars 与 banners 两个存储桶的配置、路径结构与 RLS 策略
Bucket 清单
项目中仅使用两个 Supabase Storage Bucket:
| Bucket | 公开 | 用途 | 文件格式 | 命名方式 |
|---|---|---|---|---|
avatars | 是 | 用户头像 | WebP | {user_uuid}.webp |
banners | 是 | 用户主页 Banner | WebP | {user_uuid}.webp |
两个 Bucket 均为 public(公开读取),因为头像和 Banner 需要被所有用户访问(评论区、个人主页等)。
路径结构
avatars/
└── 550e8400-e29b-41d4-a716-446655440000.webp ← 直接以用户 UUID 命名
banners/
└── 550e8400-e29b-41d4-a716-446655440000.webp ← 同理没有子目录、没有版本号、没有时间戳。每个用户在每个 Bucket 中只有一个文件,上传时直接覆盖。这是最简单也最节省空间的方案。
访问 URL
由于两个 Bucket 均为 public,可直接通过公开 URL 访问:
https://{project_ref}.supabase.co/storage/v1/object/public/avatars/{user_uuid}.webp
https://{project_ref}.supabase.co/storage/v1/object/public/banners/{user_uuid}.webp客户端获取 URL 示例:
const { data } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}.webp`)
// data.publicUrl → 完整的公开 URLRLS 策略
读取:所有人
-- avatars 和 banners 为 public bucket,读取无需策略
-- Supabase 对 public bucket 自动允许 SELECT写入:仅 VIP 会员
上传权限通过 RLS 策略控制,只有 VIP 未过期的用户才能上传。非 VIP 用户调用上传接口会收到 403。
-- 用户只能上传/覆盖自己的头像,且必须是 VIP
create policy "VIP users can upload own avatar"
on storage.objects for insert
with check (
bucket_id = 'avatars'
and auth.uid()::text = (storage.filename(name)) -- 文件名必须是自己的 UUID
and exists (
select 1 from public.profiles
where id = auth.uid()
and vip_expiration_date > now()
)
);
-- upsert(覆盖)需要同时有 insert + update 策略
create policy "VIP users can update own avatar"
on storage.objects for update
using (
bucket_id = 'avatars'
and auth.uid()::text = (storage.filename(name))
and exists (
select 1 from public.profiles
where id = auth.uid()
and vip_expiration_date > now()
)
);
-- banners 同理,仅修改 bucket_id删除:仅本人
create policy "Users can delete own avatar"
on storage.objects for delete
using (
bucket_id = 'avatars'
and auth.uid()::text = (storage.filename(name))
);文件限制
| 限制项 | 值 | 说明 |
|---|---|---|
| 格式 | WebP | 客户端上传前转换 |
| 最大尺寸 | 2 MB | 头像和 Banner 均适用 |
| 命名 | {uuid}.webp | 不允许其他命名 |
文件大小限制可在 Supabase Dashboard → Storage → Policies 中配置,也可通过 RLS 策略中的 octet_length 检查实现。