---
title: Files and images
description: Uploads kept with their records, under their rules, and images resized on request.
section: Data
order: 6
---

# Files and images

<p class="lead">A <code>file</code> field keeps an upload with its record, under the same rules. Images are resized, cropped and converted when asked for, so a thumbnail 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 make the record and upload in one step
await photos.createWithFile({ caption: "Sports day" }, "image", file);
```

Over HTTP it is `POST /api/files/{collection}/{id}/{field}`, multipart, in a part called `file`. Uploading is a change to the record, so its update rule decides; reading the file is reading the record, so its view rule does.

## Images at the size they're shown

```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` (the default: all of it, inside the box) or `cover` (fill the box, cropped) |
| `format` | `jpeg`, `png` or `webp` |
| `q` | Quality, for `jpeg` and `webp` |

A resized image is made once and kept, keyed by the file and the size, so a new upload never shows the old picture. The kept copies can be deleted at any time; they are made again when asked for.

A file that isn't public is fetched with the sign-in: `fileObjectUrl(id, field)` gives an address an `<img>` can use, and `fileText(id, field)` its text.

## Where files are kept

On disk, beside the database. For more than one server, set a bucket on any S3-compatible service (AWS, Cloudflare R2, MinIO, Backblaze) in the environment. Every upload then goes to the bucket too, and a server that doesn't have 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=…
```

## How much may be stored

A project can have a storage allowance, and each person one too, set in the admin UI. An upload that would go past it is refused before it's written. Without one, there's no limit.

The admin UI shows an image as itself in its record, and lets you replace or remove it.
