File Upload & Processing
Accept files through forms and APIs, store them as assets, and process them safely in the background.
Uploads come in two flavors: a user dropping a file into your page, and a system pushing a file to your API. Both end up as cluster assets, and heavy processing belongs in the queue. This tutorial covers the whole pipeline.
Upload via a Page (User-Facing)
- Build a form with
state_formand a file-capable input from the state elements — see the form pattern in Block Templates. - On submit, read the form with
state_form_get; the file arrives with its name, type, and size. - Store it via the asset upload API (
POST /api/v1/_system/assets/stream) or a file-handling block, intopublic(world-readable) orsystem_assets(private) — choose per public vs private. - Record the asset guid/path on a document so you can find it later (e.g.
photoon a profile document).
Upload via an API (Machine-Facing)
- Register
on_startup_register_uri_postwith your upload URI — see the upload template. - Validate: content type, size limit, required metadata. Reject with a 400 before storing anything.
- Store as an asset; reply 200 with
{"guid": "...", "path": "..."}so the caller can reference it. - Scope it deliberately — uploads are never
*publicby accident; use*loggedinor a custom scope with a secret in the path.
Processing: Do It in the Background
Resizing, parsing, importing, converting — never inline in the request. The pattern:
- The upload flow stores the file and enqueues a processing job with
function_custom_execute_from_queue— see Queue. - A queue worker flow picks it up: reads the file with
file_from_assets, transforms it (e.g. resize/convert with the image blocks), writes results back to a document or a new asset. - Mark progress on the job document (
status: queued → processing → done|failed) so users can poll or a page can show state. - Log failures to the console and storage — the error-pins pattern from Block Execution.
Safety Rules
- Never trust the client's filename for the stored path — generate your own.
- Cap file size at the API (the asset API rejects oversize with
FILE_TOO_BIG) and validate type by content, not extension. - Keep user uploads out of
publicunless they are truly public — private folders + scoped serving, per Serving Protected Files. - Import pipelines should be idempotent: re-running the same file updates the same output document (upsert by source guid).
