---
title: Files and images
description: Uploads kept with their records, under their rules, and images resized on request.
section: Data
order: 7
---

# Files and images

<p class="lead">A <code>file</code> field stores an upload with its record, under the same rules. Images are resized, cropped and converted on request, so a thumbnail only costs a thumbnail's bytes.</p>

## Uploading

```ts title="app.ts"
const photos = sluurp.collection("photos");
await photos.upload(record.id, "image", input.files[0]);

// or create the record and upload in one step
await photos.createWithFile({ caption: "Sports day" }, "image", file);
```

Over HTTP: `POST /api/files/{collection}/{id}/{field}`, multipart, with a part named `file`. Uploading counts as updating the record (update rule applies); downloading counts as viewing it (view rule applies).

## Resized images

```ts title="app.ts"
img.src = photos.fileUrl(record.id, "image", { w: 320, h: 320, fit: "cover" });
img.srcset = photos.srcset(record.id, "image", [400, 800, 1200]);
```

| Parameter | |
|---|---|
| `w`, `h` | Width and height, up to 4000 |
| `fit` | `contain` (default: fit inside the box) or `cover` (fill the box, cropped) |
| `format` | `jpeg`, `png` or `webp` |
| `q` | Quality, for `jpeg` and `webp` |

Each variant is generated once and cached, keyed by file and size, so a new upload never serves a stale image. The cache can be cleared at any time; variants are regenerated on demand.

Private files need auth: `fileObjectUrl(id, field)` returns a URL an `<img>` can use, and `fileText(id, field)` returns the contents as text.

## Storage

Files are stored on disk next to the database, in the data folder's `storage/`, or wherever `SLUURP_FILES` says; see [Storage](/docs/deploying#storage). For multiple servers, configure an S3-compatible bucket (AWS, Cloudflare R2, MinIO, Backblaze) via environment variables. Uploads are then copied to the bucket, and a server missing a file fetches it from there:

```sh title="Terminal"
SLUURP_S3_BUCKET=school-files
SLUURP_S3_ENDPOINT=https://<account>.r2.cloudflarestorage.com
SLUURP_S3_REGION=auto
SLUURP_S3_ACCESS_KEY_ID=…
SLUURP_S3_SECRET_ACCESS_KEY=…
```

## Quotas

Set a storage quota per project and per user in the admin UI. Uploads that would exceed it are rejected before being written. No quota means no limit.

The admin UI previews images in their records and lets you replace or remove them.
