今天透過 Supabase Storage ,快速實作使用者的頭像上傳功能。
在開始之前,需要在 Supabase 中設定檔案儲存功能,建立 Storage Bucket。
在 Supabase Dashboard > Storage > New Bucket 中建立名為 avatars
的儲存桶:
-- supabase/migrations/20250101000000_setup_storage.sql
-- 建立頭像儲存桶
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);
-- 設定儲存桶的存取政策
CREATE POLICY "Avatar images are publicly accessible" ON storage.objects
FOR SELECT USING (bucket_id = 'avatars');
CREATE POLICY "Users can upload their own avatar" ON storage.objects
FOR INSERT WITH CHECK (
bucket_id = 'avatars'
AND auth.uid()::text = (storage.foldername(name))[1]
);
CREATE POLICY "Users can update their own avatar" ON storage.objects
FOR UPDATE USING (
bucket_id = 'avatars'
AND auth.uid()::text = (storage.foldername(name))[1]
);
// lib/supabase/users-client.ts
export const userProfileService = {
// 上傳頭像圖片
async uploadAvatar(file: File): Promise<string | null> {
const supabase = createClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) throw new Error("使用者未登入");
// 取得檔案副檔名
const fileExt = file.name.split(".").pop();
const fileName = `${user.id}.${fileExt}`;
const filePath = `${user.id}/${fileName}`;
// 上傳檔案到 Supabase Storage
const { error: uploadError } = await supabase.storage
.from("avatars")
.upload(filePath, file, { upsert: true });
if (uploadError) {
console.error("上傳頭像失敗:", uploadError);
throw uploadError;
}
// 取得公開存取網址
const { data: publicUrlData } = supabase.storage
.from("avatars")
.getPublicUrl(filePath);
return publicUrlData.publicUrl;
},
};
身份驗證檢查
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) throw new Error("使用者未登入");
確保只有登入的使用者才能上傳頭像。
檔案路徑
const fileExt = file.name.split(".").pop();
const fileName = `${user.id}.${fileExt}`;
const filePath = `${user.id}/${fileName}`;
檔案上傳
const { error: uploadError } = await supabase.storage
.from("avatars")
.upload(filePath, file, { upsert: true });
upsert: true
表示如果檔案已存在就覆蓋,這樣使用者更換頭像時不會產生重複檔案取得公開網址
const { data: publicUrlData } = supabase.storage
.from("avatars")
.getPublicUrl(filePath);
取得可以在網頁上顯示的公開網址。
// components/user-profile-form.tsx
export function UserProfileForm() {
// 處理頭像上傳
const handleAvatarChange = async (
event: React.ChangeEvent<HTMLInputElement>
) => {
const file = event.target.files[0];
try {
// 上傳頭像到 Supabase Storage
const publicUrl = await userProfileService.uploadAvatar(file);
if (publicUrl) {
// 更新使用者資料中的頭像網址
const updatedProfile = await userProfileService.updateProfile({
avatar_url: publicUrl,
});
if (updatedProfile) {
setProfile(updatedProfile);
toast.success("頭像上傳成功!");
}
}
} catch (error) {
console.error("上傳頭像失敗:", error);
toast.error("上傳頭像失敗,請稍後再試。");
}
};
}
啟動開發伺服器
npm run dev
確認 Supabase 連線
.env.local
中的 Supabase 設定測試不同的圖片格式:
在 Supabase Dashboard 中驗證。
Storage 檢查
使用者資料檢查
avatar_url
欄位已更新為正確的公開網址透過這個頭像上傳功能,使用者能夠建立更完整的個人檔案。
... to be continued
有任何想討論歡迎留言,或需要指正的地方請鞭大力一點,歡迎訂閱、按讚加分享,分享給想要提升開發效率的朋友