存储

通过 PhotosPicker 将图片上传到 Supabase Storage。

上传示例是一个 Example(示例) 功能:选择照片,将其上传到限定于当前登录用户的私有 Supabase Storage 桶,并显示逐任务进度。当你要添加自己的文件功能时,可照搬它。

工作原理

UploadsRepository 直接与 Supabase Storage 通信(没有单独的数据源——没有需要隔离的行映射), 并返回一个 AsyncStream<UploadStatus>,让视图渲染实时进度:

enum UploadStatus: Sendable {
    case progress(Double)
    case completed(UploadedFile)
    case failed(String)
}

流程:UploadView 中的 PhotosPicker 把所选图片数据交给 UploadViewModel,后者调用 UploadsRepository.upload(data:fileName:contentType:userId:),并依据发出的 UploadStatus 值驱动 UI。

文件去向

上传写入 sample-uploads 桶,位于按用户划分的路径下,以便 RLS 按文件夹限定访问:

sample-uploads/<user-id>/<timestamp>-<short-uuid>-<sanitized-filename>

用户 ID 会被转为小写并作为第一段路径。文件名会被清洗为 [A-Za-z0-9.-_],上传使用 upsert: false(每次上传都是一个新对象)。

iOS 应用仅使用 Storage——它不写入 sample_uploads 行(Expo 原版会写)。若你需要 可查询的上传索引,可加一张表并在 completed 后插入,照搬 数据层 的仓库形态。

存储桶与策略

创建私有桶(在 Supabase 配置 中也有):

insert into storage.buckets (id, name, public, file_size_limit)
values ('sample-uploads', 'sample-uploads', false, 52428800)  -- 50 MB
on conflict (id) do nothing;

然后添加 storage.objects 策略,按每行的首段路径是否匹配调用者 UID 来放行——这正是让 按用户划分的文件夹保持私有的机制:

create policy "Users can read own sample upload objects"
  on storage.objects for select to authenticated
  using (bucket_id = 'sample-uploads'
    and (storage.foldername(name))[1] = auth.uid()::text);

create policy "Users can upload own sample upload objects"
  on storage.objects for insert to authenticated
  with check (bucket_id = 'sample-uploads'
    and (storage.foldername(name))[1] = auth.uid()::text);

create policy "Users can update own sample upload objects"
  on storage.objects for update to authenticated
  using (bucket_id = 'sample-uploads'
    and (storage.foldername(name))[1] = auth.uid()::text)
  with check (bucket_id = 'sample-uploads'
    and (storage.foldername(name))[1] = auth.uid()::text);

create policy "Users can delete own sample upload objects"
  on storage.objects for delete to authenticated
  using (bucket_id = 'sample-uploads'
    and (storage.foldername(name))[1] = auth.uid()::text);

注销账户时的清理

delete-account Edge Function 会列出 sample-uploads/<user-id>/ 下的全部内容,并在删除 auth 用户之前移除它们,因此不会留下孤立对象。没有该桶的项目会被视作「无需清理」。参见 身份认证

显示图片

应用其他地方显示远程图片时,使用 RemoteImage——它是对 Kingfisher 的轻量封装,负责异步 加载、缓存与占位/错误状态。参见 UI 组件

换成你自己的存储桶

  • 构建 UploadsRepository 时传入 bucketName: 即可更改桶名(默认为 sample-uploads)。
  • 为你的桶 id 重建这四条 storage.objects 策略。
  • 保留 <user-id>/… 路径前缀,foldername 的 RLS 检查才继续生效;或重写策略以匹配你自己的 布局。
  • 对于公共资源,创建一个 public 桶并去掉 SELECT 策略——但绝不要把按用户划分的桶设为公开。

相关页面

On this page