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).
Next Steps
Assets Asset paths, APIs, and access rules in depth. Queue Background processing for the heavy lifting. Block Templates The upload template this pipeline extends.Frequently asked
How do I accept file uploads in RUAL?
Two flavors: a page form with state_form and a file-capable input, read on submit with state_form_get, or a machine-facing API endpoint registered with on_startup_register_uri_post. Both store the file as a cluster asset, and you record the asset guid or path on a document so you can find it later.
How do I process uploaded files in RUAL without blocking the request?
The upload flow stores the file and enqueues a processing job with function_custom_execute_from_queue. A queue worker flow reads the file with file_from_assets, transforms it, and writes results back to a document or a new asset, marking progress on a job document so users can poll the state.
What safety rules apply to RUAL file uploads?
Never trust the client's filename for the stored path, validate the type by content and not by extension, keep user uploads out of public storage unless they are truly public, and make import pipelines idempotent by upserting on the source guid.