modo-upload
File upload support for modo applications: multipart form parsing, pluggable storage backends, and per-field validation — all driven by a single derive macro.
Features
| Feature | Default | Description |
|---|---|---|
local |
yes | Local filesystem storage via LocalStorage |
opendal |
no | S3-compatible object storage via OpenDAL (S3Config) |
Usage
Define a form struct
Derive FromMultipart on a named-field struct. Each field maps to a multipart
field by its Rust name (or #[serde(rename = "...")]).
use ;
Supported field types:
| Rust type | Multipart field |
|---|---|
UploadedFile |
required file |
Option<UploadedFile> |
optional file |
Vec<UploadedFile> |
zero or more files |
BufferedUpload |
required file (chunked reader) |
String |
required text |
Option<String> |
optional text |
any FromStr type |
required text, parsed |
Extract in a handler
Use MultipartForm<T> as an axum extractor. Text fields are auto-sanitized.
Call .validate() when T also derives modo::Validate.
use JsonResult;
use Service;
use ;
async
Register the storage backend
Build the storage backend from UploadConfig and register it as a service so
extractors can resolve it.
use ;
async
Manual file validation
UploadedFile exposes a fluent validate() builder when you need validation
outside the derive macro:
use ;
Size helper functions: kb(n), mb(n), gb(n) — return bytes as usize.
Configuration
UploadConfig is deserialized from the application config file under an
upload key:
upload:
backend: local # "local" (default) or "s3"
path: ./uploads # base directory for local backend
max_file_size: 10mb # global default; per-field #[upload(max_size)] overrides
S3 configuration (requires the opendal feature):
upload:
backend: s3
s3:
bucket: my-bucket
region: us-east-1
endpoint: "" # leave empty for AWS; set for MinIO etc.
access_key_id: AKIA...
secret_access_key: secret
S3Config fields: bucket, region, endpoint, access_key_id,
secret_access_key — all String, all default to empty.
Key Types
| Type | Description |
|---|---|
FromMultipart (trait + derive) |
Parse multipart/form-data into a struct |
MultipartForm<T> |
Axum extractor; wraps a FromMultipart type |
UploadedFile |
In-memory file with name, content-type, and bytes |
BufferedUpload |
Chunked in-memory file; provides AsyncRead via into_reader() |
FileStorage |
Trait for storing, deleting, and querying files |
StoredFile |
Result of a store operation: path and size |
UploadConfig |
Deserialized upload configuration |
StorageBackend |
Enum: Local or S3 |
storage() |
Factory function: UploadConfig → Box<dyn FileStorage> |
kb / mb / gb |
Size helper functions (return usize bytes) |