Skip to main content

attach_direct_upload

Function attach_direct_upload 

Source
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) on field: “is not a valid upload” (bad signature), “was not uploaded” (no object), or the messages of Validator::file.
  • Error::Internal (500) when neither R2_SECRET_ACCESS_KEY nor SECRET_KEY_BASE is set (the secret that signs upload keys), the STORAGE binding 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)
}