mcpField
S
supabase-storage

supabase-storage

github.com/nice-wolf-studio云与 DevOps19194 次安装53 周前更新
代码仓库

关于 supabase-storage

面向管理 Supabase 文件存储的技能,通过 Storage API 操作存储桶和文件。提供上传、下载、搜索、访问 URL 生成及访问控制相关指引。

主要功能

  1. 01创建、列出和管理存储桶
  2. 02上传、下载、移动及复制文件
  3. 03按目录或关键词列出文件
  4. 04生成公开 URL 和临时签名 URL
  5. 05说明 Storage Policies 与 RLS 访问控制

适用场景

  1. 01为应用创建并配置文件存储桶
  2. 02上传用户头像或下载存储文件
  3. 03搜索文件并批量整理对象
  4. 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负载过大文件超出大小限制
422MIME 类型无效此存储桶不允许该文件类型

安全最佳实践

  1. 对敏感数据使用私有存储桶
  2. 实施 Storage Policies(RLS)进行访问控制
  3. 设置文件大小限制以防止滥用
  4. 将 MIME 类型限制为预期的文件类型
  5. 使用签名 URL临时访问私有文件
  6. 将文件整理到用户专属文件夹中(例如,user-{uuid}/)
  7. 切勿向客户端应用公开 service role key

文件大小限制

  • 默认最大文件大小:50MB
  • 每个存储桶最多可配置为 5GB(如需更大容量,请联系 Supabase)
  • 查看存储桶设置以了解当前限制

API 文档

完整的 Supabase Storage API 文档:https://supabase.com/docs/guides/storage

评论

支持 Markdown