跳到主要内容

Bucket 管理

Bucket 是 PG 模式云存储中组织文件的基础容器。每个 Bucket 对应 storage.buckets 表中的一行,文件对象的元数据则写入 storage.objects,并通过 bucket_id 关联到所属 Bucket。

如果你熟悉 Supabase Storage,可以把这里理解为面向通用文件的 File Bucket:用于存储图片、视频、文档、压缩包、用户上传内容和静态资源。

什么时候拆分 Bucket

建议按访问模型、上传限制和业务生命周期拆分 Bucket,而不是简单按目录拆分。

场景推荐 Bucket典型路径推荐权限模型
用户头像avatars<uid>/avatar.png本人写,按业务决定是否公开读
公开静态资源public-assetslogos/cloudbase.png公开读,服务端写
用户私有文件private-files<uid>/<filename>仅本人读写
团队协作文件team-files<team_id>/<filename>JOIN 业务表判断团队成员
文章 / 订单附件article-assetsarticles/<article_id>/<filename>跟随业务对象权限

如果两类文件的访问权限、大小限制、MIME 类型限制或清理策略不同,通常应拆成不同 Bucket。

创建 Bucket

你可以通过 JS SDK、HTTP API、SQL 或管理端 SDK 创建 Bucket。下面以 JS SDK 和 SQL 为例。

JS SDK

import cloudbase from '@cloudbase/js-sdk';

const app = cloudbase.init({ env: '<env-id>' });

const { data, error } = await app.storage.createBucket('avatars', {
public: false,
fileSizeLimit: 5 * 1024 * 1024,
allowedMimeTypes: ['image/png', 'image/jpeg', 'image/webp'],
});

if (error) {
throw error;
}

console.log(data.id);

SQL

INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
VALUES (
'avatars',
'avatars',
false,
5 * 1024 * 1024,
ARRAY['image/png', 'image/jpeg', 'image/webp']
);

更多方式见 快速体验:创建 Bucket创建 Bucket HTTP API

字段说明

字段类型说明
idtextBucket 唯一标识,作为对象操作中的 bucketId 使用
nametextBucket 名称,长度不超过 100 个字符
publicboolean是否标记为公开 Bucket,默认 false
file_size_limitbigint单文件大小上限,单位为字节
allowed_mime_typestext[]允许上传的 MIME 类型白名单
owner_idtext创建者用户 ID,由服务端写入
created_attimestamptz创建时间
updated_attimestamptz更新时间

Public / Private 访问模型

public 是 Bucket 元数据中的公开标记,但不会自动绕过 RLS。文件是否真的可公开读取,仍由你写在 storage.objects 上的 SELECT Policy 决定。

Bucket 形态推荐配置访问方式适用场景
公开资源public = true + 公开读 RLSgetPublicUrl() / 签名 URL / 下载Logo、公开图片、静态资源
私有资源public = false + owner RLSdownload() / createSignedUrl()简历、订单附件、私人文件
团队资源public = false + JOIN 业务表 RLSdownload() / createSignedUrl()团队网盘、项目附件
官方素材public = true + 公开读、仅 service_rolegetPublicUrl()官方图标、运营 Banner

例如,如果希望 public = true 的 Bucket 允许匿名读取对象,可以写:

CREATE POLICY objects_public_read ON storage.objects
FOR SELECT TO anon, authenticated
USING (
EXISTS (
SELECT 1 FROM storage.buckets b
WHERE b.id = storage.objects.bucket_id
AND b.public
)
);

生产环境中通常不建议匿名写入公开 Bucket。公开读和谁能写入是两件事,应分别通过 SELECTINSERTUPDATEDELETE Policy 控制。

上传限制

Bucket 可以配置两类常用上传限制:

限制字段说明
单文件大小file_size_limit超过限制的上传会被拒绝
文件类型allowed_mime_types只允许白名单内的 MIME 类型

示例:限制头像 Bucket 只允许 5 MB 以内的常见图片格式:

UPDATE storage.buckets
SET
file_size_limit = 5 * 1024 * 1024,
allowed_mime_types = ARRAY['image/png', 'image/jpeg', 'image/webp']
WHERE id = 'avatars';

如果你的限制需要依赖用户角色或业务状态,例如「普通用户最多 5 MB,管理员最多 50 MB」,可在 RLS 中结合 metadata 做更细粒度判断。详见 常见问题:是否可以在 RLS 中限制文件大小 / MIME 类型

更新 Bucket

使用 JS SDK 更新 Bucket 配置:

const { data, error } = await app.storage.updateBucket('avatars', {
public: false,
fileSizeLimit: 10 * 1024 * 1024,
allowedMimeTypes: ['image/png', 'image/jpeg', 'image/webp'],
});

if (error) {
throw error;
}

console.log(data.updatedAt);

也可以通过 SQL 直接更新:

UPDATE storage.buckets
SET file_size_limit = 10 * 1024 * 1024
WHERE id = 'avatars';

删除 Bucket

删除 Bucket 前,请先确认 Bucket 下对象已清理完毕,并确认没有业务表继续引用该 Bucket 下的对象路径。

const { error } = await app.storage.deleteBucket('avatars');

if (error) {
throw error;
}

删除文件对象请使用 SDK / Storage API,不要直接 DELETE FROM storage.objects。直接删元数据会造成对象存储后端残留孤儿文件。详见 常见问题

AI 友好的设计建议

为了让 AI 编程助手更准确地生成 Bucket、路径和 RLS 策略,建议在团队文档或代码注释中固定以下约定:

约定项示例
Bucket IDavatarsprivate-filesteam-files
路径模板<uid>/avatar.png<team_id>/<filename>
权限模型owner_id = auth.uid()team_members JOIN
上传限制file_size_limit = 5242880allowed_mime_types = ['image/png']
访问方式公开资源用 getPublicUrl(),私有资源用 createSignedUrl()

推荐在每个业务 Bucket 建立一段「Bucket 契约」说明:

Bucket: avatars
用途:用户头像
路径:<uid>/avatar.png
读取:所有用户可读
写入:仅本人可上传或覆盖自己的头像
限制:5 MB;image/png、image/jpeg、image/webp

这类结构化约定能显著降低 AI 生成错误路径、错误 Bucket 或过宽 RLS 策略的概率。