Agent skill

s3-file-storage

AWS S3 (and compatible) file storage patterns. Use when implementing file uploads, image storage, document management, CDN delivery, or any cloud file storage.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/s3-file-storage

SKILL.md

S3 File Storage Patterns

Setup (boto3 + Python)

python
# storage.py
import boto3
from botocore.exceptions import ClientError
import uuid, mimetypes

s3 = boto3.client(
    "s3",
    aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
    aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
    region_name=settings.AWS_REGION,
    # For S3-compatible services (Cloudflare R2, MinIO):
    # endpoint_url=settings.S3_ENDPOINT_URL,
)
BUCKET = settings.S3_BUCKET
CDN_URL = settings.CDN_URL  # e.g. "https://cdn.example.com"

Upload Patterns

Direct upload from server

python
async def upload_file(file: UploadFile, folder: str = "uploads") -> str:
    """Upload file, return public URL."""
    # Validate
    MAX_SIZE = 10 * 1024 * 1024  # 10MB
    ALLOWED_TYPES = {"image/jpeg", "image/png", "image/webp", "application/pdf"}

    content = await file.read()
    if len(content) > MAX_SIZE:
        raise ValueError(f"File too large (max {MAX_SIZE // 1024 // 1024}MB)")
    if file.content_type not in ALLOWED_TYPES:
        raise ValueError(f"File type not allowed: {file.content_type}")

    # Generate unique key
    ext = mimetypes.guess_extension(file.content_type) or ".bin"
    key = f"{folder}/{uuid.uuid4()}{ext}"

    s3.put_object(
        Bucket=BUCKET,
        Key=key,
        Body=content,
        ContentType=file.content_type,
        CacheControl="max-age=31536000",  # 1 year cache for immutable files
    )

    return f"{CDN_URL}/{key}"

Presigned URL (client uploads directly — no server bottleneck)

python
def generate_upload_url(filename: str, content_type: str, folder: str = "uploads") -> dict:
    """Client uploads directly to S3 — bypasses your server entirely."""
    ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else "bin"
    key = f"{folder}/{uuid.uuid4()}.{ext}"

    url = s3.generate_presigned_url(
        "put_object",
        Params={
            "Bucket": BUCKET,
            "Key": key,
            "ContentType": content_type,
            "ContentLength": 5 * 1024 * 1024,  # Max 5MB
        },
        ExpiresIn=300,  # URL valid for 5 minutes
    )
    return {
        "upload_url": url,
        "key": key,
        "public_url": f"{CDN_URL}/{key}",
    }

# FastAPI endpoint
@router.post("/upload-url")
async def get_upload_url(
    filename: str = Query(...),
    content_type: str = Query(...),
    user: User = Depends(get_current_user),
):
    return generate_upload_url(filename, content_type, folder=f"users/{user.id}")

Client-side direct upload

typescript
async function uploadFile(file: File): Promise<string> {
  // 1. Get presigned URL from your API
  const { upload_url, public_url } = await fetch('/api/upload-url?' + new URLSearchParams({
    filename: file.name,
    content_type: file.type,
  })).then(r => r.json())

  // 2. Upload directly to S3 (no server in the middle)
  await fetch(upload_url, {
    method: 'PUT',
    body: file,
    headers: { 'Content-Type': file.type },
  })

  return public_url
}

Delete

python
async def delete_file(key: str):
    try:
        s3.delete_object(Bucket=BUCKET, Key=key)
    except ClientError as e:
        logger.error(f"S3 delete failed for {key}: {e}")

def extract_key_from_url(url: str) -> str:
    """Extract S3 key from CDN URL."""
    return url.replace(f"{CDN_URL}/", "")

Image Resizing (on upload)

python
from PIL import Image
import io

def resize_image(content: bytes, max_size: tuple[int, int] = (1920, 1080)) -> bytes:
    img = Image.open(io.BytesIO(content))
    img.thumbnail(max_size, Image.LANCZOS)
    if img.mode in ("RGBA", "P"):
        img = img.convert("RGB")
    output = io.BytesIO()
    img.save(output, format="JPEG", optimize=True, quality=85)
    return output.getvalue()

Bucket Policy (public read for CDN)

json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Sid": "PublicRead",
    "Effect": "Allow",
    "Principal": "*",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::your-bucket/*"
  }]
}

Rules

  • NEVER store files on local disk in production (not portable, not scalable)
  • Validate file type server-side (don't trust Content-Type from client)
  • Validate file size before uploading (not after reading all bytes into RAM)
  • Use presigned URLs for large files (avoids server memory/bandwidth)
  • Generate unique keys (UUID) — never use original filenames (path traversal risk)
  • Set CacheControl headers for immutable files (images don't change after upload)
  • Delete from S3 when deleting the record in DB (no orphaned files)
  • Use lifecycle rules to auto-delete tmp/ folder after 24h

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results