Supabase存储桶管理 #

一、创建存储桶 #

1.1 使用Dashboard创建 #

text
Dashboard > Storage > Create a new bucket

配置项
├── Name: 存储桶名称
│   └── 只能包含小写字母、数字、连字符
│
├── Public bucket: 是否公开
│   └── 公开桶任何人都可以读取
│
├── File size limit: 文件大小限制
│   └── 例如: 1MB, 5MB, 10MB
│
└── Allowed MIME types: 允许的文件类型
    └── 例如: image/*, application/pdf

1.2 使用API创建 #

typescript
// 创建公开存储桶
const { data, error } = await supabase.storage.createBucket('avatars', {
  public: true,
  fileSizeLimit: 1024 * 1024, // 1MB
  allowedMimeTypes: ['image/png', 'image/jpeg', 'image/gif'],
})

// 创建私有存储桶
const { data, error } = await supabase.storage.createBucket('documents', {
  public: false,
  fileSizeLimit: 10 * 1024 * 1024, // 10MB
})

1.3 使用SQL创建 #

sql
-- 插入存储桶记录
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);

二、存储桶配置 #

2.1 更新存储桶配置 #

typescript
// 更新存储桶设置
const { data, error } = await supabase.storage.updateBucket('avatars', {
  public: true,
  fileSizeLimit: 2 * 1024 * 1024, // 2MB
  allowedMimeTypes: ['image/*'],
})

2.2 获取存储桶信息 #

typescript
// 获取所有存储桶
const { data, error } = await supabase.storage.listBuckets()

// 获取单个存储桶
const { data, error } = await supabase.storage.getBucket('avatars')

2.3 清空存储桶 #

typescript
// 清空存储桶所有文件
const { data, error } = await supabase.storage.emptyBucket('temp-uploads')

2.4 删除存储桶 #

typescript
// 删除存储桶(必须先清空)
await supabase.storage.emptyBucket('old-bucket')
const { data, error } = await supabase.storage.deleteBucket('old-bucket')

三、权限策略配置 #

3.1 公开读取策略 #

sql
-- 允许所有人读取
CREATE POLICY "Public Read Access"
ON storage.objects FOR SELECT
USING (bucket_id = 'avatars');

3.2 认证用户上传策略 #

sql
-- 只有认证用户可以上传
CREATE POLICY "Authenticated users can upload"
ON storage.objects FOR INSERT
WITH CHECK (
    bucket_id = 'uploads' AND 
    auth.role() = 'authenticated'
);

3.3 用户文件隔离策略 #

sql
-- 用户只能访问自己文件夹下的文件
CREATE POLICY "Users can access own files"
ON storage.objects FOR ALL
USING (
    bucket_id = 'user-files' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

-- 解释:
-- storage.foldername(name) 返回路径的文件夹数组
-- [1] 获取第一个文件夹名(用户ID)
-- auth.uid()::text 转换当前用户ID为文本

3.4 完整权限策略示例 #

sql
-- 为user-uploads桶设置完整策略

-- 1. 用户可以查看自己的文件
CREATE POLICY "Users can view own files"
ON storage.objects FOR SELECT
USING (
    bucket_id = 'user-uploads' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

-- 2. 用户可以上传文件到自己的文件夹
CREATE POLICY "Users can upload to own folder"
ON storage.objects FOR INSERT
WITH CHECK (
    bucket_id = 'user-uploads' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

-- 3. 用户可以更新自己的文件
CREATE POLICY "Users can update own files"
ON storage.objects FOR UPDATE
USING (
    bucket_id = 'user-uploads' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

-- 4. 用户可以删除自己的文件
CREATE POLICY "Users can delete own files"
ON storage.objects FOR DELETE
USING (
    bucket_id = 'user-uploads' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

四、常见存储桶配置 #

4.1 头像存储桶 #

sql
-- 创建头像存储桶
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
VALUES (
    'avatars',
    'avatars',
    true,
    1048576, -- 1MB
    ARRAY['image/png', 'image/jpeg', 'image/gif', 'image/webp']
);

-- 权限策略
CREATE POLICY "Anyone can view avatars"
ON storage.objects FOR SELECT
USING (bucket_id = 'avatars');

CREATE POLICY "Users can upload own avatar"
ON storage.objects FOR INSERT
WITH CHECK (
    bucket_id = 'avatars' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

CREATE POLICY "Users can update own avatar"
ON storage.objects FOR UPDATE
USING (
    bucket_id = 'avatars' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

4.2 文档存储桶 #

sql
-- 创建文档存储桶
INSERT INTO storage.buckets (id, name, public, file_size_limit)
VALUES (
    'documents',
    'documents',
    false,
    10485760 -- 10MB
);

-- 权限策略
CREATE POLICY "Users can manage own documents"
ON storage.objects FOR ALL
USING (
    bucket_id = 'documents' AND 
    auth.uid()::text = (storage.foldername(name))[1]
);

4.3 产品图片存储桶 #

sql
-- 创建产品图片存储桶
INSERT INTO storage.buckets (id, name, public)
VALUES ('product-images', 'product-images', true);

-- 管理员可以管理所有图片
CREATE POLICY "Admins can manage all product images"
ON storage.objects FOR ALL
USING (
    bucket_id = 'product-images' AND 
    EXISTS (
        SELECT 1 FROM profiles
        WHERE id = auth.uid() AND role = 'admin'
    )
);

-- 所有人可以查看
CREATE POLICY "Anyone can view product images"
ON storage.objects FOR SELECT
USING (bucket_id = 'product-images');

五、使用Dashboard管理策略 #

5.1 策略编辑器 #

text
Dashboard > Storage > [Bucket] > Policies

功能
├── 查看现有策略
├── 创建新策略
├── 编辑策略
├── 删除策略
└── 使用模板

5.2 策略模板 #

text
Dashboard提供常用模板
├── Allow public read access
├── Allow authenticated read access
├── Allow public upload
├── Allow authenticated upload
├── Allow user to read own files
├── Allow user to upload to own folder
└── Allow user to update own files

六、存储桶管理函数 #

6.1 检查存储桶是否存在 #

typescript
async function bucketExists(name: string): Promise<boolean> {
  const { data, error } = await supabase.storage.getBucket(name)
  return !error && !!data
}

6.2 获取存储桶统计 #

typescript
async function getBucketStats(bucketName: string) {
  const { data: files, error } = await supabase.storage
    .from(bucketName)
    .list()
  
  if (error) return null
  
  const totalSize = files?.reduce((sum, file) => 
    sum + (file.metadata?.size || 0), 0
  ) || 0
  
  return {
    fileCount: files?.length || 0,
    totalSize,
    totalSizeMB: (totalSize / 1024 / 1024).toFixed(2),
  }
}

6.3 批量创建存储桶 #

typescript
async function setupBuckets() {
  const buckets = [
    { name: 'avatars', public: true, limit: 1024 * 1024 },
    { name: 'documents', public: false, limit: 10 * 1024 * 1024 },
    { name: 'images', public: true, limit: 5 * 1024 * 1024 },
  ]
  
  for (const bucket of buckets) {
    const exists = await bucketExists(bucket.name)
    if (!exists) {
      await supabase.storage.createBucket(bucket.name, {
        public: bucket.public,
        fileSizeLimit: bucket.limit,
      })
    }
  }
}

七、错误处理 #

7.1 常见错误 #

typescript
const errorMessages: Record<string, string> = {
  'Bucket not found': '存储桶不存在',
  'Bucket already exists': '存储桶已存在',
  'Invalid bucket name': '存储桶名称无效',
  'storage quota exceeded': '存储配额已满',
  'File size exceeded': '文件大小超限',
  'Invalid mime type': '文件类型不允许',
}

function handleStorageError(error: any): string {
  return errorMessages[error.message] || error.message
}

八、最佳实践 #

8.1 存储桶命名规范 #

text
命名建议
├── 使用小写字母
├── 使用连字符分隔
├── 简短有意义
└── 示例: avatars, user-documents, product-images

8.2 权限设计原则 #

text
权限设计原则
├── 最小权限原则
├── 按需开放访问
├── 用户数据隔离
├── 敏感文件私有
└── 公开资源只读

九、总结 #

存储桶管理要点:

操作 方法
创建 createBucket(name, options)
更新 updateBucket(name, options)
获取 getBucket(name)
列出 listBuckets()
清空 emptyBucket(name)
删除 deleteBucket(name)

下一步,让我们学习文件上传下载!

最后更新:2026-03-28