modo_upload/lib.rs
1//! File upload support for modo applications.
2//!
3//! Provides multipart form parsing, in-memory buffering, pluggable storage
4//! backends, and file validation. The `#[derive(FromMultipart)]`
5//! macro generates the boilerplate for mapping multipart fields to struct fields.
6//!
7//! # Features
8//!
9//! - `local` (default) — local filesystem storage via [`storage::local::LocalStorage`].
10//! - `opendal` — S3-compatible object storage via `storage::opendal::OpendalStorage`
11//! and the `S3Config` configuration type.
12//!
13//! # Quick start
14//!
15//! Define a form struct, register the storage backend as a service, then
16//! extract the form in a handler:
17//!
18//! ```rust,ignore
19//! use std::sync::Arc;
20//! use modo::{AppConfig, Json, JsonResult, Service};
21//! use modo_upload::{FileStorageDyn, FromMultipart, MultipartForm, UploadConfig, UploadedFile, storage};
22//! use serde::Deserialize;
23//!
24//! #[derive(Default, Deserialize)]
25//! struct AppSettings {
26//! #[serde(flatten)]
27//! core: AppConfig,
28//! #[serde(default)]
29//! upload: UploadConfig,
30//! }
31//!
32//! #[derive(FromMultipart)]
33//! struct UploadForm {
34//! #[upload(max_size = "5mb", accept = "image/*")]
35//! avatar: UploadedFile,
36//! name: String,
37//! }
38//!
39//! #[modo::handler(POST, "/upload")]
40//! async fn upload(
41//! file_storage: Service<Arc<dyn FileStorageDyn>>,
42//! form: MultipartForm<UploadForm>,
43//! ) -> JsonResult<()> {
44//! let stored = file_storage.store("avatars", &form.avatar).await?;
45//! println!("stored at {}", stored.path);
46//! Ok(Json(()))
47//! }
48//!
49//! #[modo::main]
50//! async fn main(
51//! app: modo::app::AppBuilder,
52//! config: AppSettings,
53//! ) -> Result<(), Box<dyn std::error::Error>> {
54//! let file_storage = storage(&config.upload)?;
55//! app.config(config.core).service(file_storage).run().await
56//! }
57//! ```
58
59pub use modo_upload_macros::FromMultipart;
60
61mod config;
62mod extractor;
63mod file;
64mod from_multipart;
65pub mod storage;
66mod stream;
67mod validate;
68
69#[cfg(feature = "opendal")]
70pub use config::S3Config;
71pub use config::{StorageBackend, UploadConfig};
72pub use extractor::MultipartForm;
73pub use file::UploadedFile;
74pub use from_multipart::FromMultipart;
75pub use storage::{FileStorage, FileStorageDyn, FileStorageSend, StoredFile, storage};
76pub use stream::BufferedUpload;
77pub use validate::{gb, kb, mb};
78
79/// Internal helpers exposed for use by generated code. Not public API.
80#[doc(hidden)]
81pub mod __internal {
82 pub use crate::validate::mime_matches;
83 pub use axum;
84}