# `PhoenixKitWeb.FileController`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.164/lib/phoenix_kit_web/controllers/file_controller.ex#L1)

File serving controller with signed URL support.

Handles secure file retrieval with token-based authentication and cache headers.

# `info`

Get file information without serving the file.

## Request

    GET /api/files/:file_uuid/info

## Response

Success (200):
    {
      "file_uuid": "uuid",
      "original_filename": "photo.jpg",
      "mime_type": "image/jpeg",
      "file_type": "image",
      "size": 1234567,
      "status": "active",
      "variants": [
        {
          "variant_name": "original",
          "mime_type": "image/jpeg",
          "size": 1234567,
          "width": 1920,
          "height": 1080,
          "url": "/file/uuid/original/token"
        }
      ]
    }

# `serve_manifest`

Serve the DZI manifest for an image, generating it lazily if it doesn't
exist yet.

## Request

    GET /tiles/:token/:dzi_filename

where `dzi_filename` is `"<file_uuid>.dzi"` and `token` is the signed
per-file token from `URLSigner.generate_token(file_uuid, "dzi")`.
Returns the XML manifest describing the image's dimensions and tile
config — Tessera's `generate_manifest/3` produces it on first request,
subsequent requests serve from storage.

The token gates BOTH manifest and tile generation: without it, the
endpoint is a 404. The MediaBrowser emits manifest URLs only when
`storage_tile_generation_enabled` is on, so unauthenticated callers
can't trigger lazy ImageMagick work by guessing UUIDs.

# `serve_tile`

Serve a single DZI tile, generating it lazily if it doesn't exist yet.

## Request

    GET /tiles/:token/:files_segment/:level/:tile_filename

where `token` is the signed per-file token (same one used by
`serve_manifest/2`), `files_segment` is `"<file_uuid>_files"`,
`level` is the integer zoom level, and `tile_filename` is
`"<col>_<row>.<ext>"`. This matches the layout Tessera writes to
storage and the URL convention OpenSeadragon derives from a DZI
manifest's base URL (token in the path survives that derivation;
query-string tokens don't).

# `show`

Serve a file variant by ID with signed URL token.

## Request

    GET /file/:file_uuid/:variant/:token

## Parameters

- `file_uuid`: UUID of the file
- `variant`: Variant name (e.g., "original", "thumbnail", "medium")
- `token`: Signed token for authentication

## Response

Success (200):
- File streamed to client with appropriate headers:
  - `Cache-Control: public, max-age=31536000, immutable` (1 year)
  - `ETag: "md5-hash"`
  - `Content-Type: <mime-type>`
  - `Content-Disposition: inline; filename="..."`

Not Modified (304):
- Returned when request includes `If-None-Match` matching the file ETag

Error (401):
    "Invalid or expired token"

Error (404):
    "File or variant not found"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
