无论图片、视频还是文档,其存储与权限管控都是后端绕不开的负担------自建存储、接口与鉴权,周期长、成本高。RDS Supabase文件存储(File Storage)把这一切收敛为一项开箱即用的能力:底层对接华为云OBS对象存储,几行代码完成上传下载,权限管控一并到位,无需自建文件服务器。
权限管控与图片处理:结合Auth的行级安全(RLS)策略,用规则界定"谁能看、谁能传、谁能删",把文件级权限下沉到数据库而非应用代码;内置图片缩放,按需生成尺寸,无需自建图片处理服务。相比直连OBS,它打通Auth身份、RLS权限与存储,省去自建鉴权与服务端胶水代码。策略生效,文件即按权限存取------公开图片随取随用,私密文档按身份隔离。
本篇以电商场景两个存储桶为线索------公开的产品图片桶product-images、私有的产品规格文档桶product-specs,依次讲清基本概念与映射、控制台管理、安全策略与SDK操作。
Part 01 --- 基本概念与前置准备
基本概念
|-------------|------------------------|
| 概念 | 说明 |
| 存储桶(Bucket) | 存储文件的顶层容器,可设为公开或私有 |
| 文件夹 | 存储桶内的文件目录层级 |
| 文件 | 图片、文档等二进制资源,存放于OBS对象存储 |
**存储桶与OBS桶的映射关系:**一个华为云OBS桶承载一个RDS Supabase实例的全部存储,每个Supabase存储桶对应OBS桶下的1个子目录(底层OBS兼容S3标准,用户经Supabase SDK访问)。
图1 存储桶与OBS桶的映射关系
**文件类型与大小约束:**Storage支持上传任意类型的二进制文件(图片、文档、音视频、压缩包等),可按桶限定MIME类型;单文件大小上限也可按桶配置。
前置条件
|------------|-----------------------------------------|
| 条件 | 说明 |
| 华为云实名认证 | 使用存储功能前必须完成,否则无法接入OBS |
| OBS桶与AK/SK | OBS控制台创建桶,IAM控制台创建具有OBS权限的AK/SK(最小权限原则) |
| 网络可达 | 客户端能访问Supabase应用的IP与端口 |
| 同区域 | OBS桶建议与RDS Supabase实例处于同一区域 |
功能启用
文件存储底层对接OBS,使用前需在华为云管理控制台启用存储功能,流程如下图:
图2 存储功能启用流程
详细操作步骤参见"开启或关闭Storage存储配置"。
Part 02 --- 通过控制台管理文件
本部分通过控制台完成存储桶创建与文件上传管理,可视化操作,无需写代码。
第1步 创建存储桶
在控制台创建存储桶,相当于在OBS桶下新建一个子目录,按业务隔离存放文件。
-
登录RDS Supabase控制台(Dashboard)。
-
在控制台左侧边栏选择Storage。
-
点击New bucket,输入业务桶名称(对应OBS桶下的子目录名),可配置文件大小限制与允许的MIME类型。
图3 Supabase Dashboard创建存储桶指引
图4 Supabase Dashboard创建存储桶配置项界面
两个示例桶的配置:
|----------------|----------------------|------------|----------------------------------------------|
| 存储桶 | 公开性 | 文件大小限制 | 允许MIME类型 |
| product-images | 公开(开启Public bucket) | 10MB | image/png, image/jpeg, image/webp, image/gif |
| product-specs | 私有(不开启Public bucket) | 50MB | application/pdf |
- 点击Create完成。
图5 演示示例Bucket桶列表
第2步 上传和管理文件
-
点击目标存储桶进入。
-
创建文件夹(Create folder)。
图6 Supabase Dashboard创建存储桶文件夹入口 -
上传文件(Upload files)。
图7 Supabase Dashboard上传文件 -
通过文件右侧菜单下载、获取URL、移动、删除。
Part 03 --- 安全策略设置
Storage Policies基于PostgreSQL行级安全(RLS):storage.objects 表须启用RLS,策略才能生效,可用以下语句启用并确认:
-- 启用 storage.objects 的行级安全
ALTER TABLE storage.objects ENABLE ROW LEVEL SECURITY;
-- 确认 RLS 已启用(relrowsecurity 为 t 即已启用)
SELECT relname, relrowsecurity FROM pg_class WHERE relname = 'objects';
通过控制台设置
-
登录RDS Supabase控制台(Dashboard)。
-
在控制台左侧导航栏选择Storage,进入存储管理页面,再点击"Policies"页签。
图8 Supabase Dashboard存储桶安全策略配置列表 -
选择目标存储桶,点击New Policy,选择使用模板或自定义。
图9 Supabase Dashboard存储桶新增自定义安全策略 -
配置策略名称、允许操作、目标角色。
策略定义示例:
• product-images(公开桶,允许公共读取):策略名称"Public read for product-images",允许SELECT,目标角色anon,策略定义bucket_id = 'product-images'。上传/删除需另配INSERT/DELETE策略,详见SQL。
• product-specs(私有桶,仅认证用户访问自己的文件夹):建议拆为上传、读取、删除三条策略,策略定义均为 bucket_id = 'product-specs' 且仅限本人文件夹(详见下文 SQL)。
**注意:**公开桶的所有文件对全网可见,仅用于存放可公开的内容(如产品图片),切勿存放敏感数据。
- 点击Save policy完成。
通过SQL语句设置
**为商品图片桶product-images设置公共读取:**任何人(含未登录访客)均可通过URL直接查看,适用于官网、商品详情页等公开展示场景。
-- SQL------为公开桶设置公共读取
CREATE POLICY "Public read for product-images"
ON storage.objects FOR SELECT
USING ( bucket_id = 'product-images' );
**允许认证用户上传到公开桶。**仅有读取策略时,通过SDK上传会被RLS拒绝,需补充认证用户的上传策略。
-- SQL------允许认证用户上传到公开桶
CREATE POLICY "Authenticated upload for product-images"
ON storage.objects FOR INSERT
TO authenticated
WITH CHECK ( bucket_id = 'product-images' );
**允许认证用户删除公开桶文件。**同理,缺删除策略时,通过SDK删除会被RLS拒绝,需补充认证用户的删除策略。
-- SQL------允许认证用户删除公开桶文件
CREATE POLICY "Authenticated delete for product-images"
ON storage.objects FOR DELETE
TO authenticated
USING ( bucket_id = 'product-images' );
**仅允许认证用户访问自己的文件夹。**为product-specs设置用户隔离:登录用户只能在自己用户ID命名的文件夹下读写删文件,看不到、改不了也删不了他人的文档,适用于私密文档存放。
-- 上传:仅在自己的文件夹内
CREATE POLICY "Authenticated upload in user folder"
ON storage.objects FOR INSERT
TO authenticated
WITH CHECK(bucket_id = 'product-specs' AND auth.uid()::text = (storage.foldername(name))[1]);
-- 读取:仅在自己的文件夹内
CREATE POLICY "Authenticated read in user folder"
ON storage.objects FOR SELECT
TO authenticated
USING(bucket_id = 'product-specs' AND auth.uid()::text = (storage.foldername(name))[1]);
-- 删除:仅在自己的文件夹内
CREATE POLICY "Authenticated delete in user folder"
ON storage.objects FOR DELETE
TO authenticated
USING(bucket_id = 'product-specs' AND auth.uid()::text = (storage.foldername(name))[1]);
上例假设存储桶内以用户ID(auth.uid())作为顶层文件夹名,按文件夹隔离访问。
Part 04 --- 通过SDK操作文件
SDK操作前请确保已按Part 03配好策略(上传需INSERT、读取(download/list)需SELECT、删除需DELETE)。以下仍以product-images(公开)、product-specs(私有)为例,假设已用createClient(SUPABASE_URL, anonKey)初始化客户端(访问IP与端口见Part 01前置条件)。
创建存储桶
电商场景分别建公开的产品图片桶与私有的产品规格文档桶,使图片可公开浏览、规格文档仅限授权访问。
// JavaScript------创建存储桶
// 创建公开存储桶
const { data, error } = await supabase.storage
.createBucket('product-images', { public: true })
if (error) { console.error('创建失败', error); return }
// 创建私有存储桶
const { error: err2 } = await supabase.storage.createBucket('product-specs', { public: false })
if (err2) { console.error('创建失败', err2); return }
上传文件
将本地文件上传到指定桶路径。如运营为新品上传图片至product-images桶,存入OBS后可供商品详情页展示。
// JavaScript------上传文件
const file = event.target.files[0]
// 'phones/p10.png' 为桶内对象路径(目录/文件名)
const { data, error } = await supabase.storage
.from('product-images')
.upload('phones/p10.png', file, {
cacheControl: '3600',
upsert: false, // false: 同名不覆盖,存在则报错;true: 覆盖(需额外配 UPDATE 策略,否则静默失败)
})
if (error) {
console.error('上传失败', error)
return
}
下载文件
取回文件二进制内容(Blob)转URL供页面渲染,如商品详情页展示product-images中的产品图。
// JavaScript------下载文件
const { data, error } = await supabase.storage
.from('product-images')
.download('phones/p10.png')
if (error) { console.error('下载失败', error); return }
// data 是 Blob 对象
const url = URL.createObjectURL(data)
获取公开URL / 签名URL
取得访问链接:公开桶用公开URL嵌入页面展示,私有桶用限时签名URL------允许临时查看又不长期暴露地址。
// JavaScript------获取访问链接
// 公开存储桶 --- 直接获取URL
const { data } = supabase.storage
.from('product-images')
.getPublicUrl('phones/p10.png')
// URL格式:https://{访问IP}:{访问端口}/storage/v1/object/public/product-images/phones/p10.png
// 私有存储桶 --- 生成临时签名URL(3600秒有效,按需调整)
// 路径顶层文件夹须为当前登录用户ID,与 Part 03 文件夹隔离策略一致
const { data: { user } } = await supabase.auth.getUser()
const { data, error } = await supabase.storage
.from('product-specs')
.createSignedUrl(`${user.id}/pricing.pdf`, 3600)
getPublicUrl返回的URL仅在存储桶设为public时可直接访问;私有存储桶需通过createSignedUrl生成临时签名URL。
图片缩放
对公开桶图片按需生成缩略图或指定尺寸,无需自建图片处理服务:getPublicUrl附带transform参数即可,原图与各尺寸互不影响。
// JavaScript------图片缩放
// 获取 200x200 缩略图(cover 裁剪模式,质量80)
const { data } = supabase.storage
.from('product-images')
.getPublicUrl('phones/p10.png', {
transform: { width: 200, height: 200, resize: 'cover', quality: 80 }
})
// data.publicUrl 即缩放后的图片地址,可直接用于商品列表缩略图
列出文件
列出某桶某目录下的文件清单。后台管理商品图片库时,可分页查看某产品目录下全部图片供运营挑选。
// JavaScript------列出文件
const { data, error } = await supabase.storage
.from('product-images')
.list('phones', {
limit: 100,
sortBy: { column: 'name', order: 'asc' }
})
if (error) { console.error('列取失败', error); return }
删除文件
从桶中移除文件。产品下架或图片替换时删除旧图,避免残留冗余文件;支持批量删除。需已配置DELETE策略,否则会被RLS拒绝。
// JavaScript------删除文件
const { data, error } = await supabase.storage
.from('product-images')
.remove(['phones/p10.png', 'phones/p10-detail.png'])
if (error) { console.error('删除失败', error); return }