Supabase存储概述 #
一、存储服务架构 #
1.1 什么是Supabase Storage #
text
Supabase Storage
├── 对象存储服务
├── 类似AWS S3
├── 支持大文件存储
├── 内置CDN加速
├── 图片处理功能
└── 细粒度权限控制
1.2 存储架构 #
text
┌─────────────────────────────────────────────────────────────┐
│ 客户端应用 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Supabase Storage API │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Upload │ │ Download │ │ Delete │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ List │ │ Sign URL │ │ Transform │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 存储后端 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Buckets │ │ Objects │ │ CDN │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
1.3 核心概念 #
| 概念 | 说明 |
|---|---|
| Bucket | 存储桶,文件的容器 |
| Object | 存储对象,单个文件 |
| Path | 文件路径 |
| Public | 公开访问 |
| Private | 私有访问,需要签名URL |
二、存储桶 #
2.1 存储桶类型 #
text
存储桶类型
├── Public Bucket
│ ├── 公开访问
│ ├── 无需认证
│ ├── 适合公开资源
│ └── 如: 头像、公开图片
│
└── Private Bucket
├── 需要认证
├── 使用签名URL
├── 适合私有文件
└── 如: 用户文档、私密资料
2.2 创建存储桶 #
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
})
2.3 Dashboard创建 #
text
Dashboard > Storage
创建步骤
├── 1. 点击 "Create a new bucket"
├── 2. 输入桶名称
├── 3. 选择是否公开
├── 4. 设置文件大小限制
├── 5. 设置允许的MIME类型
└── 6. 保存
三、存储桶权限 #
3.1 RLS策略 #
sql
-- 查看存储桶表
SELECT * FROM storage.buckets;
-- 查看对象表
SELECT * FROM storage.objects;
-- 存储桶权限策略
CREATE POLICY "Public Access"
ON storage.objects FOR SELECT
USING (bucket_id = 'avatars');
-- 用户只能访问自己的文件
CREATE POLICY "Users can access own files"
ON storage.objects FOR ALL
USING (
bucket_id = 'documents' AND
auth.uid()::text = (storage.foldername(name))[1]
);
3.2 常用策略模板 #
sql
-- 公开读取
CREATE POLICY "Public Read Access"
ON storage.objects FOR SELECT
USING (bucket_id = 'public-bucket');
-- 认证用户上传
CREATE POLICY "Authenticated users can upload"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'user-uploads' AND
auth.role() = 'authenticated'
);
-- 用户只能管理自己的文件
CREATE POLICY "Users manage own files"
ON storage.objects FOR ALL
USING (
bucket_id = 'user-files' AND
auth.uid()::text = (storage.foldername(name))[1]
);
四、文件路径规范 #
4.1 路径结构 #
text
文件路径结构
├── bucket_id/folder/subfolder/filename.ext
│
├── 示例:
│ ├── avatars/user-123/profile.jpg
│ ├── documents/user-456/report.pdf
│ └── images/products/product-1/main.jpg
│
└── 建议: 使用用户ID作为文件夹名
4.2 路径最佳实践 #
text
路径命名建议
├── 使用用户ID隔离文件
│ └── user-123/avatar.jpg
│
├── 使用日期组织文件
│ └── 2024/01/15/document.pdf
│
├── 使用有意义的文件夹
│ └── products/product-1/images/main.jpg
│
└── 避免特殊字符
└── 使用字母、数字、连字符、下划线
五、存储限制 #
5.1 默认限制 #
| 限制 | 免费计划 | Pro计划 |
|---|---|---|
| 存储空间 | 1GB | 100GB |
| 文件大小 | 50MB | 可配置 |
| 带宽 | 5GB/月 | 250GB/月 |
5.2 配置限制 #
typescript
// 创建桶时设置限制
const { data, error } = await supabase.storage.createBucket('uploads', {
public: false,
fileSizeLimit: 5 * 1024 * 1024, // 5MB
allowedMimeTypes: ['image/*', 'application/pdf'],
})
六、存储配额管理 #
6.1 查看使用情况 #
typescript
// 获取存储使用情况
const { data, error } = await supabase.storage.listBuckets()
// 计算总大小
let totalSize = 0
for (const bucket of data || []) {
const { data: files } = await supabase.storage
.from(bucket.name)
.list()
files?.forEach(file => {
totalSize += file.metadata?.size || 0
})
}
console.log(`Total storage used: ${totalSize / 1024 / 1024} MB`)
6.2 清理未使用文件 #
sql
-- 查找孤立文件(用户已删除)
SELECT o.name, o.bucket_id
FROM storage.objects o
LEFT JOIN auth.users u ON u.id::text = (storage.foldername(o.name))[1]
WHERE u.id IS NULL;
-- 删除孤立文件
DELETE FROM storage.objects
WHERE id IN (
SELECT o.id
FROM storage.objects o
LEFT JOIN auth.users u ON u.id::text = (storage.foldername(o.name))[1]
WHERE u.id IS NULL
);
七、CDN配置 #
7.1 CDN缓存 #
text
Supabase存储内置CDN
├── 自动全球分发
├── 边缘节点缓存
├── 减少延迟
└── 提高访问速度
7.2 缓存控制 #
typescript
// 上传时设置缓存头
const { data, error } = await supabase.storage
.from('images')
.upload('image.jpg', file, {
cacheControl: '3600', // 缓存1小时
upsert: true,
})
八、安全最佳实践 #
8.1 文件验证 #
typescript
// 验证文件类型
function validateFileType(file: File, allowedTypes: string[]): boolean {
return allowedTypes.some(type => {
if (type.endsWith('/*')) {
return file.type.startsWith(type.slice(0, -1))
}
return file.type === type
})
}
// 验证文件大小
function validateFileSize(file: File, maxSize: number): boolean {
return file.size <= maxSize
}
// 上传前验证
async function uploadFile(bucket: string, path: string, file: File) {
if (!validateFileType(file, ['image/*', 'application/pdf'])) {
throw new Error('Invalid file type')
}
if (!validateFileSize(file, 5 * 1024 * 1024)) {
throw new Error('File too large')
}
return supabase.storage.from(bucket).upload(path, file)
}
8.2 路径安全 #
typescript
// 安全的文件名
function sanitizeFilename(filename: string): string {
return filename
.toLowerCase()
.replace(/[^a-z0-9.-]/g, '-')
.replace(/-+/g, '-')
}
// 安全路径
function buildSafePath(userId: string, filename: string): string {
const safeName = sanitizeFilename(filename)
const timestamp = Date.now()
return `${userId}/${timestamp}-${safeName}`
}
九、监控与日志 #
9.1 存储日志 #
text
Dashboard > Logs > Storage
日志内容
├── 上传操作
├── 下载操作
├── 删除操作
├── 错误信息
└── 访问统计
9.2 使用统计 #
text
Dashboard > Reports
统计信息
├── 存储使用量
├── 带宽使用
├── 请求数量
└── 错误率
十、总结 #
存储服务要点:
| 概念 | 说明 |
|---|---|
| Bucket | 存储桶容器 |
| Public | 公开访问 |
| Private | 私有访问 |
| RLS | 权限控制策略 |
| CDN | 全球加速 |
下一步,让我们学习存储桶管理!
最后更新:2026-03-28