2.7 KiB
2.7 KiB
文件存储
创建存储桶
通过 SQL 迁移创建存储桶:
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);
关键上传方法
必须使用 ArrayBuffer(通过 base64-arraybuffer)进行所有文件上传。使用 Blob、File 或 FormData 会导致 400 InvalidRequest "No content provided" 错误。
注意:必须通过友好的 Toast 交互提醒用户数据操作结果,特别是操作失败的情况。
重要约束:必须在前端上传文件。禁止在云函数(Edge Functions)中上传文件,因为云函数上传的文件无法获取公网访问 URL,会导致前端无法正常访问和显示文件。所有文件上传操作必须在前端(浏览器环境)中进行。
记得在
package.json中添加base64-arraybuffer依赖。
import { decode } from 'base64-arraybuffer';
// ✅ 从 base64 字符串上传(必须检查 error)
const { data, error } = await supabase.storage
.from('avatars')
.upload('public/avatar1.png', decode('base64FileData'), {
contentType: 'image/png'
});
if (error) throw new Error(`上传失败: ${error.message}`);
// 从文件输入上传(先转换为 ArrayBuffer)
const reader = new FileReader();
reader.onload = async (e) => {
const base64 = (e.target?.result as string).split(',')[1];
const { data, error } = await supabase.storage
.from('bucket-name')
.upload(`path/${file.name}`, decode(base64), {
contentType: file.type,
});
if (error) {
console.error('上传失败:', error.message);
return;
}
};
reader.readAsDataURL(file);
// ❌ 错误做法 — 会导致 400 错误
const formData = new FormData();
formData.append('file', file);
await supabase.storage.from('bucket').upload('path', formData); // ❌
await supabase.storage.from('bucket').upload('path', blob); // ❌
await supabase.storage.from('bucket').upload('path', file); // ❌
存储 RLS 策略
-- 创建存储桶的 RLS 策略
CREATE POLICY anon_select_avatars ON storage.objects
FOR SELECT USING (bucket_id = 'avatars');
CREATE POLICY users_insert_avatars ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'avatars' AND
auth.role() = 'authenticated'
);
Edge Functions 存储限制
重要:存储操作应在前端执行,而非 Edge Functions。Edge Functions 使用私有网络 URL,无法提供文件的公开访问。
存储模式限制
不要在 storage 模式中执行以下操作:
- 创建自定义表或函数
- 删除现有表或函数
- 在现有存储表上创建索引
- 对
storage.migrations执行破坏性操作
管理文件访问请在 public 模式中创建辅助函数。