Cadmus
v0.1.2

Guides

File uploads

Storing files on disk or in S3, Azure or Google Cloud - file fields on forms, uploads through the API, and resumable uploads.

Set uploads.storage_url in the settings to turn uploads on. The address chooses where files are kept:

AddressWhereCargo feature
file:///absolute/patha folder on the serverbuilt in
s3://bucket/prefixAmazon S3 and compatible servicess3
az://container/prefixAzure Blob Storageazure
gs://bucket/prefixGoogle Cloud Storagegcs
uploads: UploadSettings {
    storage_url: Some("file:///var/lib/blog/media".into()),
    ..Default::default()
},

Cloud credentials are read from the usual environment variables (AWS_ACCESS_KEY_ID, AWS_REGION, AWS_ENDPOINT, AZURE_STORAGE_ACCOUNT_NAME, ...). For the cloud backends, turn the feature on: cadmus = { version = "0.1", features = ["s3"] }.

#Limits

uploads.max_file_bytes (default 1 GiB) and uploads.max_chunk_bytes (default 8 MiB, for resumable uploads).

#File fields on forms

cadmus generate model Product name:string photo:file

A file field holds a file's id. On the admin form it is a file picker that shows the current file, a preview for PNG, JPEG, GIF, WebP and AVIF images, and "Remove the file". Lists show the file's name, linked to it. Anyone who may view the record may download its file. Files from a form that is then rejected are removed.

A hand-written integer field becomes a file field with ModelOptions::file_fields(["photo"]).

Files sent through an admin form must finish within server.request_timeout (30 seconds by default), so very large files should go through the API. Replacing or removing a file on a form leaves the old one stored (it is still listed at /api/files).

#Through the API

RequestWhat it does
POST /api/files (multipart form, field file)uploads a file; answers 201 with its record
GET /api/files, GET /api/files/{id}file details
GET /api/files/{id}/contentdownloads the file
DELETE /api/files/{id}deletes it

To fill a file field through the API, upload the file, then send its id in the field. Uploads need the files permissions (see Accounts and permissions).

#Resumable uploads

Large files can be sent in pieces, and the upload continues after a dropped connection:

  1. POST /api/uploads with {"filename", "size", "content_type"} gives an upload id.
  2. PATCH /api/uploads/{id} with the header Upload-Offset: N and the next bytes, in order. A wrong offset gets 409 with the right one.
  3. GET /api/uploads/{id} tells how much has arrived: that is where a client resumes.
  4. The piece that completes the file answers 201 with the file's record.
  5. DELETE /api/uploads/{id} cancels.

Uploads survive server restarts and can continue on another server. Stored names are chosen by the server; the original file name is kept only as a label.

#In your own code

Storage (put, get, delete, sink) and Uploads are available for your own handlers; see the API reference.