pub struct Multipart<const LIMIT: usize>(pub MultipartForm);Expand description
Extractor for a multipart/form-data body of at most LIMIT bytes.
It reads HTML forms with enctype="multipart/form-data" (file inputs)
and API clients (curl -F avatar=@me.png). Take the text fields with
MultipartForm::form or MultipartForm::text and the files with
MultipartForm::file; check files with
Validator::file before storing them.
The whole body is read into memory, then split in place: file parts are
slices of that one buffer. The rejection is a response, not an
Error in the handler:
- 400 when the content type is not
multipart/form-datawith aboundary, the body cannot be read, or it is malformed; - 413 “The request is too large (maximum is 6 MB)” when
Content-Lengthannounces more thanLIMIT(before anything is read) or as soon as the body passes it.
Errors are HTML pages for browsers (Accept: text/html, feature html)
and JSON (like ApiError) otherwise.
Free plan: keep LIMIT in the tens of MB. A Worker has 128 MB, the body
is held in memory while R2 gets a copy, and Cloudflare refuses request
bodies over 100 MB on the Free plan before they reach the Worker.
Splitting takes about 1.2 ms of CPU per 10 MB in WebAssembly (memchr’s
substring search, measured in V8). For a single large file, stream the
raw body with store_body instead.
§Examples
use axum::{extract::State, response::Redirect};
use ocre::storage::{self, Multipart, Rules};
use ocre::{Ctx, Result, Validator};
use serde::Deserialize;
const IMAGE: Rules = Rules { max_bytes: 10 * 1024 * 1024, content_types: &["image/png", "image/jpeg"] };
const FORM_LIMIT: usize = IMAGE.max_bytes as usize + 1024 * 1024;
#[derive(Deserialize)]
struct NewPhoto {
title: String,
}
async fn create(State(ctx): State<Ctx>, Multipart(mut form): Multipart<FORM_LIMIT>) -> Result<Redirect> {
let fields: NewPhoto = form.form()?;
let image = form.file("image"); // None when no file was chosen
let mut v = Validator::new();
v.required("title", &fields.title);
if let Some(image) = &image {
v.file("image", image, &IMAGE);
}
v.finish()?;
if let Some(image) = image {
let stored = storage::store(&ctx, "photos/image", image).await?;
}
Ok(Redirect::to("/photos"))
}Tuple Fields§
§0: MultipartForm