跳至主要內容

Supabase Storage 檔案管理完整教學

Supabase Storage 檔案管理完整教學

Supabase Storage 是 Supabase 平台提供的物件儲存服務,底層基於 S3 相容的 API,讓你能輕鬆在應用程式中處理圖片、影片、文件等各種檔案。搭配 Supabase 的 Row Level Security(RLS),可以精細控制檔案的存取權限。

核心概念

  • Bucket(儲存桶):類似資料夾的最頂層容器,可設定公開或私有
  • Object(物件):儲存桶中的實際檔案
  • Public Bucket:任何人都可以透過 URL 存取檔案
  • Private Bucket:需要身份驗證或簽名 URL 才能存取

初始設定

npm install @supabase/supabase-js
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';

export const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);

建立 Bucket

透過 SQL(推薦用於版本控制)

-- 建立公開 bucket
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);

-- 建立私有 bucket
INSERT INTO storage.buckets (id, name, public)
VALUES ('documents', 'documents', false);

上傳檔案

async function uploadFile(file: File, bucket: string, path: string) {
  const { data, error } = await supabase.storage
    .from(bucket)
    .upload(path, file, {
      cacheControl: '3600',
      upsert: false,
    });

  if (error) throw error;
  return data;
}

const file = event.target.files[0];
await uploadFile(file, 'avatars', `users/${userId}/avatar.jpg`);

取得檔案 URL

// 公開 bucket 的永久 URL
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('users/123/avatar.jpg');

console.log(data.publicUrl);

// 私有 bucket 的簽名 URL(1 小時有效)
const { data, error } = await supabase.storage
  .from('documents')
  .createSignedUrl('reports/2024-annual.pdf', 3600);

列出檔案

async function listUserFiles(userId: string) {
  const { data, error } = await supabase.storage
    .from('documents')
    .list(`users/${userId}`, {
      limit: 20,
      offset: 0,
      sortBy: { column: 'created_at', order: 'desc' },
    });

  if (error) throw error;
  return data;
}

刪除與移動檔案

// 刪除檔案
await supabase.storage
  .from('avatars')
  .remove(['users/123/old-avatar.jpg']);

// 移動(重新命名)
await supabase.storage
  .from('documents')
  .move('drafts/report.pdf', 'published/report.pdf');

// 複製
await supabase.storage
  .from('documents')
  .copy('templates/base.docx', `users/${userId}/new-doc.docx`);

Row Level Security(RLS)設定

-- 允許已登入的使用者上傳到自己的資料夾
CREATE POLICY "使用者可上傳自己的檔案"
ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (
  bucket_id = 'avatars' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

-- 允許公開讀取 avatars bucket
CREATE POLICY "公開讀取頭像"
ON storage.objects
FOR SELECT
TO public
USING (bucket_id = 'avatars');

-- 使用者只能刪除自己的檔案
CREATE POLICY "使用者可刪除自己的檔案"
ON storage.objects
FOR DELETE
TO authenticated
USING (
  bucket_id = 'avatars' AND
  (storage.foldername(name))[1] = auth.uid()::text
);

圖片轉換(Image Transformation)

const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('users/123/avatar.jpg', {
    transform: {
      width: 100,
      height: 100,
      resize: 'cover',
      format: 'webp',
      quality: 80,
    },
  });

實用工具函數

export async function uploadAvatar(userId: string, file: File): Promise<string> {
  const ext = file.name.split('.').pop();
  const path = `${userId}/avatar.${ext}`;

  await supabase.storage.from('avatars').remove([path]);

  const { error } = await supabase.storage
    .from('avatars')
    .upload(path, file, { upsert: true });

  if (error) throw new Error(`上傳失敗: ${error.message}`);

  const { data } = supabase.storage
    .from('avatars')
    .getPublicUrl(path, {
      transform: { width: 200, height: 200, resize: 'cover' },
    });

  return data.publicUrl;
}

總結

Supabase Storage 提供了完整的檔案管理解決方案,從基本的上傳下載、權限控制到圖片轉換,都有簡潔的 API 支援。結合 RLS 政策,你可以用幾行 SQL 實現精細的存取控制,而不需要額外開發後端邏輯。

分享這篇文章