Rocket - Upload media file

Hi everyone,

I am currently trying to upload file (images and videos) with Rocket 0.4.
But I have some problems...

When I save an image, I can't read it with my computer (see attachment at the end)

Back :

#[post("/media/<filename>", data = "<media>")]
fn add_media_for_agency(
    filename: &RawStr,
    media: Data,
) {
    let mut buffer = vec![];
    if let Err(e) = media.stream_to(&mut buffer) {
        return ...;
    }

    let filepath = get_static_filepath(filename.into());

    if let Err(e) = std::fs::write(filepath, content) {
        return ...;
    }
}

Front :

uploadMedias(files: File[]) {
    for (let file of files) {

      const formData = new FormData();

      formData.append("media", file);

      this.httpClient.post<...>(`${environment.api_base_url}/media/${file.name}`, formData).subscribe({

        next: data => console.log(data),

        error: data => console.log(data)

      });

    }

  }

As I have seen on some links, I have missed something.
Maybe multipart format ? Maybe something else ?

Could someone help me please ?

Links :

Maybe try opening it with a regular image viewer? sometimes the built-in one in vscode doesn't work so well.

From the docs of Data

Type representing the data in the body of an incoming request.
...
This type is not usually used directly.

What you're doing is writing the entire body of the request into the file, instead of the data of the file that's inside the body. You'll want to use https://crates.io/crates/rocket-multipart-form-data/0.9.6 (I think) or upgrade to 0.5 which has support for multipart forms included.

Thank you for yours answers.

@lukechu : I have already try to save with 'image' crate but that does not work, because of FormData structure

How hard is the crate 'rocket-multipart-form-data' (v0.9) to understand, here my code if someone needs it.
Note that for multipart_form_data.files.remove("media"), 'media' is the field send by the front in the FormData.

#[post("/media", data = "<data>")]
fn add_media_for_agency(
    data: Data,
    content_type: &ContentType,
) -> Result<..., ...> {
    let options = MultipartFormDataOptions::with_multipart_form_data_fields(vec![
        MultipartFormDataField::file("media"),
    ]);

    let mut multipart_form_data = match MultipartFormData::parse(content_type, data, options) {
        Ok(r) => r,
        Err(e) => {
            return Err(...)
        }
    };

    match multipart_form_data.files.remove("media") {
        Some(mut media) => {
            let file = media.remove(0);

            let filename = match file.file_name {
                Some(n) => n,
                None => match file.content_type {
                    Some(t) => format!(
                        "{}.{}",
                        generate_random_filename(),
                        t.subtype()
                    ),
                    None => todo!(),
                },
            };

            let filepath = get_media_filepath(filename.into());

            if let Some(p) = filepath.parent() {
                if let Err(e) = std::fs::create_dir_all(p) {
                    return Err(...);
                }
            }

            match std::fs::read(media.into()) {
                Ok(content) => match std::fs::write(filepath, content) {
                    Ok(_) => Ok(()),
                    Err(e) => Err(...),
                },
                Err(e) => Err(...),
            }
        }
        None => Err(...),
    }
}

Looks alright to me, one thing that can clean your code up a little bit is to use copy in std::fs - Rust instead of calling fs::read followed by fs::write