Search code examples
rustrust-cargorust-tokiorust-rocket

How to properly upload files using Rocket?


Looking for some variants how to upload file and meta-data in one request, I find out that I can use multipart/form-data content-type.

In rust rocket documentation, I found example where TempFile was used, but it's saved without any obvious file extension. Just like data-file.

Then I created a route and trying to get a file name (full, with extension), and then I saw .name and .raw_name methods.

First returns pure name without extension and the second returns &FileName, that I can perform to str (but it's equivalent to .name()).

Then I saw dangerous_unsafe_unsanitized_raw() method in documentation, and I don't get it, should I use it or no?

Maybe there is a more proper way how to upload files? (Like with Data<'_> but there are no methods to use with filename at all, and I should know proper file size.. ) or I shouldn't save files with obvious extension..?

Any suggestions, please?

Update:

#[post("/upload", data = <file>)]
async fn upload_route(data: TempFile<'_>) -> (Status, Value) {
    //I need to save file (ex. test.zip), somehow. For properly saving i need:
    //1) Filename (-> test)
    //2) File extension (-> .zip)
    let pure_name = file.name().unwrap();        //returns "test", but not .zip
    let file_name = file.raw_name().unwrap();    //returns &FileName("test.zip")
    let file_name = file_name.as_str().unwrap(); //returns "test" (I expected "test.zip"!)
    let real_full_name = file.raw_name().unwrap().dangerous_unsage_unsanitized_raw().as_str();
    data.persist_to("temp/dir/to/<filename>").await?;

    (Status::Ok, Value::String(String::from("File successfully saved")))   
}

Solution

  • Looking at the source code file.name.unwrap() is equivalent to file.raw_name().unwrap().as_str().unwrap(). And the FileName::as_str docs do state that they strip the extension as part of the process for sanitizing the name.

    You can get a safe name with as_str and use the content type to get a valid extension:

    fn get_name (file: &TempFile<'_>) -> Option<String> {
        format!("{}.{}", file.name()?, file.content_type()?.extension()?)
    }