pub fn store_bytes(
ctx: &Ctx,
prefix: &str,
filename: &str,
content_type: &str,
bytes: Vec<u8>,
) -> impl Future<Output = Result<Attachment>> + Send + use<>Expand description
Stores bytes built by the app (a generated report, an export) like store.
The object gets a new random key under prefix; filename is cleaned
up (no directories or control characters, at most 200 characters) and
content_type normalized (lowercase, no parameters; empty becomes
application/octet-stream). The returned Attachment records all
four, ready to save with columns.
Free plan: one R2 class A operation (1M free per month); copying 10 MB to R2 takes about 0.15 ms of CPU.
§Errors
Error::Internal (500) when the STORAGE
binding is missing (the message shows the STORAGE entry to add
to cloudflare.config.ts) or the R2 write fails.
§Examples
use axum::extract::State;
use ocre::{Ctx, Result, storage};
use serde::Deserialize;
#[derive(Deserialize)]
struct Post {
id: i64,
title: String,
}
// POST /exports: writes every post to a CSV kept in R2.
async fn export(State(ctx): State<Ctx>) -> Result<String> {
let posts: Vec<Post> = ctx.db()?.all("SELECT id, title FROM posts ORDER BY id", vec![]).await?;
let mut csv = String::from("id,title\n");
for post in posts {
csv.push_str(&format!("{},\"{}\"\n", post.id, post.title.replace('"', "\"\"")));
}
let file = storage::store_bytes(&ctx, "exports", "posts.csv", "text/csv", csv.into_bytes()).await?;
Ok(file.key)
}