Skip to content

Document Upload Service

A multi-file document upload endpoint using S3 storage with metadata tracking.

Full Example

app.py
from datetime import datetime, timezone

from fastapi import Depends, FastAPI
from filestore import FileField, FileStore, S3Engine, Store, StoreConfig, UploadContext

app = FastAPI()


def document_metadata(ctx: UploadContext) -> dict:
    """Attach request context as metadata."""
    return {
        "uploaded_by": ctx.request.headers.get("X-User-ID", "anonymous"),
        "uploaded_at": datetime.now(timezone.utc).isoformat(),
        "original_name": ctx.file.filename,
        "ip": ctx.request.client.host if ctx.request.client else None,
    }


storage = FileStore(
    fields=[
        FileField(
            name="documents",
            required=True,
            max_count=10,
            config=StoreConfig(
                destination="uploads/documents",
                allowed_extensions=[".pdf", ".docx", ".xlsx", ".pptx", ".txt"],
                max_file_size=50 * 1024 * 1024,  # 50 MB per file
            ),
        ),
    ],
    engine=S3Engine(bucket="my-documents-bucket", region="us-east-1"),
    config=StoreConfig(metadata=document_metadata),
)


@app.post("/documents")
async def upload_documents(store: Store = Depends(storage)):
    return {
        "status": store.status,
        "uploaded": [
            {
                "filename": f.filename,
                "url": f.url,
                "key": f.key,       # keep for delete / presigned downloads
                "size": f.size,
                "metadata": f.metadata,
            }
            for f in store.successful_files
        ],
        "rejected": [
            {
                "filename": f.original_filename,
                "error": f.error,
            }
            for f in store.failed_files
        ],
        "total_size_mb": round(store.total_size / (1024 * 1024), 2),
    }

Key Points

  • Up to 10 files per request with max_count=10
  • 50 MB per file limit
  • Metadata callback captures upload context for audit trails
  • FileData.key is stored so documents can later be deleted (engine.delete(key)) or served via engine.presign_download(key)
  • Structured response separates successes from failures