关于 supabase-storage
面向管理 Supabase 文件存储的技能,通过 Storage API 操作存储桶和文件。提供上传、下载、搜索、访问 URL 生成及访问控制相关指引。
主要功能
- 01创建、列出和管理存储桶
- 02上传、下载、移动及复制文件
- 03按目录或关键词列出文件
- 04生成公开 URL 和临时签名 URL
- 05说明 Storage Policies 与 RLS 访问控制
适用场景
- 01为应用创建并配置文件存储桶
- 02上传用户头像或下载存储文件
- 03搜索文件并批量整理对象
- 04为私有文档生成限时访问链接
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ nice-wolf-studio/ claude-code-supabase-skills --skill supabase-storage安装后在 Claude Code 中输入/supabase-storage确认已加载。
详细介绍
Supabase 存储操作
概述
此技能通过 Supabase Storage API 提供文件存储操作。支持存储桶管理、文件上传/下载、文件列表、生成 URL 和管理访问控制。
前置条件
必需的环境变量:
export SUPABASE_URL="https://your-project.supabase.co"
export SUPABASE_KEY="your-anon-or-service-role-key"
辅助脚本: 此技能使用共享的 Supabase API 辅助脚本。请确保先加载它:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
存储桶操作
列出存储桶
获取所有存储桶:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_get "/storage/v1/bucket"
创建存储桶
创建新的存储桶:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
# Public bucket
supabase_post "/storage/v1/bucket" '{
"name": "avatars",
"public": true
}'
# Private bucket
supabase_post "/storage/v1/bucket" '{
"name": "private-documents",
"public": false,
"file_size_limit": 52428800,
"allowed_mime_types": ["image/png", "image/jpeg", "application/pdf"]
}'
获取存储桶详情
获取存储桶信息:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_get "/storage/v1/bucket/avatars"
更新存储桶
更新存储桶设置:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
curl -s -X PUT \
"${SUPABASE_URL}/storage/v1/bucket/avatars" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-H "Content-Type: application/json" \
-d '{
"public": false,
"file_size_limit": 10485760
}'
删除存储桶
删除存储桶(必须为空):
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_delete "/storage/v1/bucket/old-bucket"
清空存储桶
删除存储桶中的所有文件:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_post "/storage/v1/bucket/avatars/empty" '{}'
文件操作
上传文件
将文件上传到存储空间:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
FILE_PATH="/path/to/local/image.jpg"
STORAGE_PATH="user-123/profile.jpg"
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-F "file=@${FILE_PATH}"
使用 upsert 上传(如果文件存在则覆盖):
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-H "x-upsert: true" \
-F "file=@${FILE_PATH}"
上传时指定内容类型:
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-H "Content-Type: image/jpeg" \
-F "file=@${FILE_PATH}"
下载文件
从存储空间下载文件:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
OUTPUT_FILE="/path/to/save/downloaded.jpg"
curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-o "${OUTPUT_FILE}"
下载到标准输出(管道传递给其他命令):
curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}"
列出文件
列出存储桶中的文件:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{
"limit": 100,
"offset": 0,
"sortBy": {
"column": "name",
"order": "asc"
}
}'
列出特定文件夹中的文件:
supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{
"prefix": "user-123/",
"limit": 100
}'
搜索文件:
supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{
"search": "profile",
"limit": 50
}'
删除文件
删除单个文件:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/old-profile.jpg"
supabase_delete "/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}"
删除多个文件:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
supabase_delete "/storage/v1/object/${BUCKET_NAME}" -d '{
"prefixes": ["user-123/temp/", "user-456/draft/"]
}'
移动/重命名文件
移动或重命名文件:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
FROM_PATH="user-123/temp.jpg"
TO_PATH="user-123/final.jpg"
supabase_post "/storage/v1/object/move" '{
"bucketId": "'"${BUCKET_NAME}"'",
"sourceKey": "'"${FROM_PATH}"'",
"destinationKey": "'"${TO_PATH}"'"
}'
复制文件
将文件复制到其他位置:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
SOURCE_PATH="user-123/profile.jpg"
DEST_PATH="user-123/profile-backup.jpg"
supabase_post "/storage/v1/object/copy" '{
"bucketId": "'"${BUCKET_NAME}"'",
"sourceKey": "'"${SOURCE_PATH}"'",
"destinationKey": "'"${DEST_PATH}"'"
}'
生成 URL
获取公开 URL
获取文件的公开 URL(仅适用于公开存储桶):
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
PUBLIC_URL="${SUPABASE_URL}/storage/v1/object/public/${BUCKET_NAME}/${STORAGE_PATH}"
echo "Public URL: $PUBLIC_URL"
创建签名 URL
生成用于临时访问的签名 URL:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="private-documents"
STORAGE_PATH="user-123/secret.pdf"
EXPIRES_IN=3600 # 1 hour in seconds
supabase_post "/storage/v1/object/sign/${BUCKET_NAME}/${STORAGE_PATH}" '{
"expiresIn": '"${EXPIRES_IN}"'
}'
响应将包含:
{
"signedURL": "/storage/v1/object/sign/private-documents/user-123/secret.pdf?token=..."
}
完整 URL:
signed_path=$(supabase_post "/storage/v1/object/sign/${BUCKET_NAME}/${STORAGE_PATH}" '{"expiresIn": 3600}' | jq -r '.signedURL')
full_url="${SUPABASE_URL}${signed_path}"
echo "Signed URL: $full_url"
创建多个签名 URL
为多个文件生成签名 URL:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="private-documents"
supabase_post "/storage/v1/object/sign/${BUCKET_NAME}" '{
"paths": ["doc1.pdf", "doc2.pdf", "folder/doc3.pdf"],
"expiresIn": 3600
}'
常见模式
上传并显示进度
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="uploads"
FILE_PATH="/path/to/large-file.zip"
STORAGE_PATH="user-123/backup.zip"
echo "Uploading ${FILE_PATH}..."
curl -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-F "file=@${FILE_PATH}" \
--progress-bar -o /dev/null
if [[ $? -eq 0 ]]; then
echo "Upload successful!"
else
echo "Upload failed!"
exit 1
fi
检查文件是否存在
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
# Try to get file info
response=$(curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/info/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-w "\n%{http_code}")
http_code=$(echo "$response" | tail -n1)
if [[ $http_code -eq 200 ]]; then
echo "File exists"
else
echo "File does not exist"
fi
批量上传文件
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="uploads"
LOCAL_DIR="/path/to/files"
for file in "$LOCAL_DIR"/*; do
filename=$(basename "$file")
echo "Uploading $filename..."
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/batch/${filename}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-F "file=@${file}"
echo "✓ Uploaded $filename"
done
下载存储桶中的所有文件
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
OUTPUT_DIR="/path/to/download"
mkdir -p "$OUTPUT_DIR"
# Get file list
files=$(supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{"limit": 1000}')
# Download each file
echo "$files" | jq -r '.[].name' | while read -r filename; do
echo "Downloading $filename..."
curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${filename}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-o "${OUTPUT_DIR}/${filename}"
echo "✓ Downloaded $filename"
done
图像转换
即时转换图像(公开存储桶):
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
# Resize to width 200px
TRANSFORMED_URL="${SUPABASE_URL}/storage/v1/render/image/public/${BUCKET_NAME}/${STORAGE_PATH}?width=200"
# Resize with quality
TRANSFORMED_URL="${SUPABASE_URL}/storage/v1/render/image/public/${BUCKET_NAME}/${STORAGE_PATH}?width=200&quality=80"
# Multiple transformations
TRANSFORMED_URL="${SUPABASE_URL}/storage/v1/render/image/public/${BUCKET_NAME}/${STORAGE_PATH}?width=300&height=300&resize=contain"
访问控制
存储访问权限由 Supabase 控制台中的 Storage Policies(RLS)管理。
常见策略示例:
公开读取,已认证用户可写入:
-- Set in Supabase Dashboard > Storage > Policies
-- Allow public to read
SELECT: true for all users
-- Allow authenticated users to upload their own files
INSERT: auth.uid() = (storage.foldername(name))[1]
用户专属文件夹:
-- Users can only access files in their own folder
SELECT: auth.uid() = (storage.foldername(name))[1]
UPDATE: auth.uid() = (storage.foldername(name))[1]
DELETE: auth.uid() = (storage.foldername(name))[1]
错误处理
常见错误:
| 状态 | 错误 | 含义 |
|---|---|---|
| 400 | 存储桶名称无效 | 存储桶名称包含无效字符 |
| 404 | 未找到对象 | 文件不存在 |
| 409 | 存储桶已存在 | 已存在同名存储桶 |
| 413 | 负载过大 | 文件超出大小限制 |
| 422 | MIME 类型无效 | 此存储桶不允许该文件类型 |
安全最佳实践
- 对敏感数据使用私有存储桶
- 实施 Storage Policies(RLS)进行访问控制
- 设置文件大小限制以防止滥用
- 将 MIME 类型限制为预期的文件类型
- 使用签名 URL临时访问私有文件
- 将文件整理到用户专属文件夹中(例如,
user-{uuid}/) - 切勿向客户端应用公开 service role key
文件大小限制
- 默认最大文件大小:50MB
- 每个存储桶最多可配置为 5GB(如需更大容量,请联系 Supabase)
- 查看存储桶设置以了解当前限制
API 文档
完整的 Supabase Storage API 文档:https://supabase.com/docs/guides/storage
评论
支持 Markdown