2.7 KiB
Raw Blame History

文件存储

创建存储桶

通过 SQL 迁移创建存储桶:

INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);

关键上传方法

必须使用 ArrayBuffer(通过 base64-arraybuffer)进行所有文件上传。使用 BlobFileFormData 会导致 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 模式中创建辅助函数。