pub fn attach_direct_upload(
ctx: &Ctx,
field: &str,
signed_key: &str,
filename: &str,
rules: &Rules,
) -> impl Future<Output = Result<Attachment>> + Send + use<>Expand description
Finishes a direct upload (or a multipart_uploads one): checks the object behind signed_key against rules and returns its Attachment.
Rails’ attach(signed_blob_id). signed_key must come from
direct_upload (a key this app signed, so a client cannot claim
another record’s file); the object must exist, and its size and
content type (as R2 recorded them) must pass rules. A refused object
is deleted. filename is the name the form sends (cleaned up). Save
the attachment with columns; if that write
fails, delete the key.
Free plan: one R2 class B operation (head), plus a free delete when
the object is refused.
§Errors
Error::Invalid(422) onfield: “is not a valid upload” (bad signature), “was not uploaded” (no object), or the messages ofValidator::file.Error::Internal(500) when neitherR2_SECRET_ACCESS_KEYnorSECRET_KEY_BASEis set (the secret that signs upload keys), theSTORAGEbinding is missing, or R2 fails.
§Examples
use axum::{Form, extract::{Path, State}};
use ocre::storage::{self, Rules};
use ocre::{Ctx, IntoParam, Result};
use serde::Deserialize;
const VIDEO: Rules = Rules { max_bytes: 500 * 1024 * 1024, content_types: &["video/mp4"] };
#[derive(Deserialize)]
struct VideoForm {
video_key: String,
video_filename: String,
}
async fn update(State(ctx): State<Ctx>, Path(id): Path<i64>, Form(form): Form<VideoForm>) -> Result<String> {
let video = storage::attach_direct_upload(&ctx, "video", &form.video_key, &form.video_filename, &VIDEO).await?;
let mut values = Vec::from(storage::columns(Some(&video)));
values.push(id.into_param());
let sql = "UPDATE lessons SET video_key = ?1, video_filename = ?2, video_content_type = ?3, video_size = ?4 \
WHERE id = ?5";
if let Err(err) = ctx.db()?.execute(sql, values).await {
storage::delete(&ctx, &video.key).await?;
return Err(err);
}
Ok(video.key)
}