Skip to content

File upload

Secure upload composition with UploadFlow. Limits, cleanup, and authorization stay explicit — Hedron does not own storage or malware scanning.

Try it (simulated)

Allowlisted .txt succeeds; .exe is rejected. Docs simulation (canned files, no disk picker).

Upload a .txt or .csv file (max 64 KiB)

Docs simulation — choose a canned file (no real disk picker).

Minimal runnable app.py that reproduces this demo (real Hedron, not the docs simulator):

app.py
"""File upload demo with size/type checks. Local learning only."""

from __future__ import annotations

from fastapi import File, UploadFile

from hedron import CsrfField, FileUpload, Form, Hedron, Page, Stack, SubmitButton, Text

app = Hedron(
    title="Upload demo",
    security="standard",
    explorer="off",
    session_secret="replace-in-production",
)

MAX_BYTES = 64 * 1024
ALLOWED = {".txt", ".csv"}


@app.action("/upload", fallback="/")
async def upload(roster: UploadFile = File(...)) -> Page:
    name = roster.filename or "upload"
    suffix = "." + name.rsplit(".", 1)[-1].lower() if "." in name else ""
    if suffix not in ALLOWED:
        return Page(Text(f"Rejected type: {name}"), title="Rejected")
    data = await roster.read(MAX_BYTES + 1)
    if len(data) > MAX_BYTES:
        return Page(Text("File too large"), title="Rejected")
    preview = data[:200].decode("utf-8", errors="replace")
    return Page(
        Stack(
            Text(f"Received {name} ({len(data)} bytes)"),
            Text(preview or "(empty)"),
        ),
        title="Uploaded",
    )


@app.page("/")
def home() -> Page:
    return Page(
        Stack(
            Text("Upload a .txt or .csv file (max 64 KiB)"),
            Form(
                CsrfField(),
                FileUpload(name="roster", accept=".txt,.csv"),
                SubmitButton("Upload"),
                action=upload,
                enctype="multipart/form-data",
            ),
        ),
        title="Upload",
    )

Run without cloning the monorepo

python -m venv .venv && source .venv/bin/activate   # Windows: py -3 -m venv .venv && .\.venv\Scripts\Activate.ps1
pip install "hedron>=1.0.0" "uvicorn[standard]"
curl -fsSL https://raw.githubusercontent.com/eddiethedean/hedron/main/examples/file-upload/app.py -o app.py
uvicorn app:app --reload

Or paste the Code tab above into app.py.

Run (monorepo)

uv sync
uv run uvicorn app:app --app-dir examples/file-upload --reload

Open http://127.0.0.1:8000. Upload a small .txt or .csv (max 64 KiB in this demo).

What it shows

  • UploadFlow with explicit authorize / store / result callbacks
  • UploadField + UploadBudget (display and enforcement stay aligned)
  • Application-owned storage (no inferred filesystem layout)

Advanced — explicit @app.action / FileUpload

Lower to FileUpload, Form(enctype="multipart/form-data"), and @app.action when ejecting. See What’s new in 0.60.

Source: examples/file-upload.