Quat Docs
Framework guides

Python

Use Quat with boto3, FastAPI, Django, and Flask.

Plain Python With boto3

Install

pip install boto3 python-dotenv

Environment

S3_ENDPOINT=https://storage.example.local
S3_ACCESS_KEY=your-access-key
S3_SECRET_KEY=your-secret-key
S3_REGION=us-east-1
S3_BUCKET=my-bucket
S3_USE_PATH_STYLE_ENDPOINT=true

Client

import os
from dotenv import load_dotenv
import boto3
from botocore.config import Config

load_dotenv()

s3 = boto3.client(
    "s3",
    endpoint_url=os.environ["S3_ENDPOINT"],
    aws_access_key_id=os.environ["S3_ACCESS_KEY"],
    aws_secret_access_key=os.environ["S3_SECRET_KEY"],
    region_name=os.getenv("S3_REGION", "us-east-1"),
    config=Config(s3={"addressing_style": "path"}),
)

bucket = os.environ["S3_BUCKET"]

Object Operations

from s3_client import s3, bucket

def upload_object():
    s3.upload_file("avatar.png", bucket, "users/123/avatar.png")

def download_object():
    s3.download_file(bucket, "users/123/avatar.png", "downloaded-avatar.png")

def list_objects():
    response = s3.list_objects_v2(Bucket=bucket, Prefix="users/123/")
    for item in response.get("Contents", []):
        print(item["Key"], item["Size"])

def delete_object():
    s3.delete_object(Bucket=bucket, Key="users/123/avatar.png")

FastAPI

Install

pip install fastapi uvicorn boto3 python-multipart python-dotenv

Upload And Download

import os
import tempfile
from fastapi import FastAPI, UploadFile, HTTPException
from fastapi.responses import StreamingResponse
from s3_client import s3, bucket

app = FastAPI()

@app.post("/upload")
async def upload(file: UploadFile):
    key = f"uploads/{file.filename}"

    with tempfile.NamedTemporaryFile(delete=False) as tmp:
        tmp.write(await file.read())
        tmp_path = tmp.name

    try:
        s3.upload_file(tmp_path, bucket, key, ExtraArgs={"ContentType": file.content_type})
        return {"key": key}
    finally:
        os.unlink(tmp_path)

@app.get("/download/{key:path}")
def download(key: str):
    try:
        obj = s3.get_object(Bucket=bucket, Key=key)
    except Exception as exc:
        raise HTTPException(status_code=404, detail="Object not found") from exc

    return StreamingResponse(
        obj["Body"],
        media_type=obj.get("ContentType", "application/octet-stream"),
        headers={"Content-Disposition": f'attachment; filename="{key.split("/")[-1]}"'},
    )

Run:

uvicorn main:app --reload

Django

Install:

pip install django-storages boto3

Settings example:

INSTALLED_APPS = [
    # ...
    "storages",
]

STORAGES = {
    "default": {
        "BACKEND": "storages.backends.s3boto3.S3Boto3Storage",
        "OPTIONS": {
            "bucket_name": os.environ["S3_BUCKET"],
            "endpoint_url": os.environ["S3_ENDPOINT"],
            "access_key": os.environ["S3_ACCESS_KEY"],
            "secret_key": os.environ["S3_SECRET_KEY"],
            "region_name": os.getenv("S3_REGION", "us-east-1"),
            "addressing_style": "path",
            "default_acl": None,
            "querystring_auth": True,
        },
    },
}

Usage:

from django.core.files.storage import default_storage
from django.core.files.base import ContentFile

default_storage.save("uploads/hello.txt", ContentFile(b"Hello from Django"))

Flask

Install

pip install flask boto3 python-dotenv

Upload And Download

from flask import Flask, request, send_file, abort
from tempfile import NamedTemporaryFile
from s3_client import s3, bucket

app = Flask(__name__)

@app.post("/upload")
def upload():
    file = request.files.get("file")
    if not file:
        abort(400, "Missing file")

    key = f"uploads/{file.filename}"
    s3.upload_fileobj(file.stream, bucket, key, ExtraArgs={"ContentType": file.content_type})
    return {"key": key}

@app.get("/download/<path:key>")
def download(key):
    tmp = NamedTemporaryFile(delete=False)
    try:
        s3.download_fileobj(bucket, key, tmp)
        tmp.flush()
        return send_file(tmp.name, as_attachment=True, download_name=key.split("/")[-1])
    except Exception:
        abort(404)

Common Python Issues

SymptomFix
SignatureDoesNotMatchUse Config(s3={"addressing_style": "path"}) and verify region.
NoCredentialsErrorLoad .env before creating the boto3 client.
EndpointConnectionErrorUse the storage endpoint, not the Quat customer app URL.
Temporary URL failsConfirm that your deployment supports S3 presigned URLs.

On this page