Logo花火漫画开发文档

Bucket 配置

avatars 与 banners 两个存储桶的配置、路径结构与 RLS 策略

Bucket 清单

项目中仅使用两个 Supabase Storage Bucket:

Bucket公开用途文件格式命名方式
avatars用户头像WebP{user_uuid}.webp
banners用户主页 BannerWebP{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 → 完整的公开 URL

RLS 策略

读取:所有人

-- 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 检查实现。

On this page