从表单数据中检索原始文件内容

Emi*_* C. 5 rust rust-rocket

我正在与 Rust 和 Rocket 合作。我有一个端点可以一次上传一个文件form-data

\n
use rocket::form::{Form, FromForm};\nuse rocket::fs::TempFile;\nuse std::ffi::OsStr;\nuse std::path::{Path};\nuse uuid::Uuid;\n\n#[post("/file_upload", format = "multipart/form-data", data = "<form>")]\npub async fn file_upload(mut form: Form<Upload<\'_>>) -> std::io::Result<String> {\n  // Get raw file \n  let file_name = form.file.raw_name().unwrap().dangerous_unsafe_unsanitized_raw().as_str();name\n  // Get extension of file name\n  let extension = Path::new(file_name).extension().and_then(OsStr::to_str).unwrap(); \n  // Generate new UUID\n  let id: String = Uuid::new_v4().to_string(); \n  // Build path to save file\n  let file_path = String::from("media/temp_files") + "/" + &id + "." + extension; \n\n  // Save file\n  form.file.persist_to(file_path).await?; \n\n  Ok(String::from("Ok"))\n}\n
Run Code Online (Sandbox Code Playgroud)\n

这可行,但我将持久性、业务逻辑和 HTTP 基础设施混合在同一个模块中。我只想依靠 Rocket 来检索文件流和元数据(文件名、大小和内容类型),并将其传递给另一个负责验证、图像处理等的函数。

\n

我可以访问元数据,但我不知道如何从结构中检索缓冲内容TempFile

\n
// rocket-0.5.0-rc.2/src/fs/temp_file.rs\n[\xe2\x80\xa6]\npub enum TempFile<\'v> {\n    #[doc(hidden)]\n    File {\n        file_name: Option<&\'v FileName>,\n        content_type: Option<ContentType>,\n        path: Either<TempPath, PathBuf>,\n        len: u64,\n    },\n    #[doc(hidden)]\n    Buffered {\n        content: &\'v str,\n    }\n}\n[\xe2\x80\xa6]\n
Run Code Online (Sandbox Code Playgroud)\n

我没有看到任何方法返回它。是否缺少任何方法来检索原始文件内容?或者 Rocket 中可能有不同的结构/特征来实现这一点。

\n

Xvo*_*lks 0

只是我的评论的后续内容可以回答您的问题。

@badoken 似乎在这里提交了 PR:https ://github.com/SergioBenitez/Rocket/pull/2361

TempFile基本上这允许像这样传递结构:

#[derive(FromForm)]
struct MyForm<'r> {
    foo: &'r [u8],
}
Run Code Online (Sandbox Code Playgroud)

我仍然不确定如何从中获取元数据。我必须进一步测试并改进这个答案。

编辑:经过一番挖掘,我有以下适合我的需求的解决方案。

use rocket::form::{DataField, Form, FromFormField};
use rocket::http::{ContentType, CookieJar, Status};

use rocket::{FromForm, post};
use rocket::data::ToByteUnit;

use rocket::fs::{FileName};
use rocket::response::content::RawHtml;
use crate::utils::Md5;

pub struct File<'v> {
    file_name: Option<&'v FileName>,
    content_type: ContentType,
    data: Vec<u8>,
}

#[rocket::async_trait]
impl<'v> FromFormField<'v> for File<'v> {
    async fn from_data(field: DataField<'v, '_>) -> rocket::form::Result<'v, Self> {
        let stream = field.data.open(u32::MAX.bytes());
        let bytes = stream.into_bytes().await?;
        Ok(File {
            file_name: field.file_name,
            content_type: field.content_type,
            data: bytes.value,
        })

    }
}

#[derive(FromForm)]
pub struct UploadRequest<'r> {
    file: File<'r>,
}

#[post("/upload", data = "<req>")]
pub async fn upload(
    content_type: &ContentType,
    req: Form<UploadRequest<'_>>,
    cookies: &CookieJar<'_>,
) -> Result<RawHtml<String>, Status> {
    Ok(RawHtml(format!("file: {}, md5: {}, content-type: {} / {}",
                       req.file.file_name.unwrap().as_str().unwrap_or("Frack") ,
                       &req.file.data.md5(),
                       content_type,
                       req.file.content_type

    )))
}

Run Code Online (Sandbox Code Playgroud)

当在端点中推送小视频文件时/upload,我得到以下结果:

file: 1ae366a5-209e-4b5e-8d37-826db09090fb, md5: ae78f71db09cb7ae26607f0bd7e917a3, content-type: multipart/form-data;
boundary=--------------------------248453579191145706333860 / video/mp4
Run Code Online (Sandbox Code Playgroud)

文件名的扩展名丢失,但其类型位于文件content_type字段中。

md5 哈希确保文件未被更改。