pub fn store_body(
ctx: &Ctx,
prefix: &str,
filename: &str,
content_type: &str,
size: u64,
body: Body,
) -> impl Future<Output = Result<Attachment>> + Send + use<>Expand description
Streams body (exactly size bytes) into R2 without holding it in memory, and returns its Attachment.
For a raw request body with a Content-Length (curl -T big.zip),
where Multipart would hold the whole body
in memory. R2 needs the length up front: a body of another length makes
the write fail and nothing is stored. The file name and content type are
cleaned up like store_bytes’s; no Rules
are checked, so check size and content_type first.
Free plan: one R2 class A operation (1M free per month). Chunks still pass through WebAssembly, but memory stays flat; Cloudflare refuses request bodies over 100 MB on the Free plan.
§Errors
Error::Internal (500) when the STORAGE
binding is missing (the message shows the STORAGE entry to add
to cloudflare.config.ts), the body is not size bytes long, or the R2 write fails.
§Examples
use axum::{body::Body, extract::State, http::{HeaderMap, header}};
use ocre::{Ctx, Error, Result, storage};
const MAX: u64 = 50 * 1024 * 1024;
// PUT /backups with `curl -T backup.zip`.
async fn upload(State(ctx): State<Ctx>, headers: HeaderMap, body: Body) -> Result<String> {
let size: u64 = headers
.get(header::CONTENT_LENGTH)
.and_then(|value| value.to_str().ok()?.parse().ok())
.ok_or_else(|| Error::bad_request("Content-Length required"))?;
if size > MAX {
return Err(Error::PayloadTooLarge("The backup is too large (maximum is 50 MB)".into()));
}
let backup = storage::store_body(&ctx, "backups", "backup.zip", "application/zip", size, body).await?;
Ok(backup.key)
}