File upload in Actix Web

Hello

I am trying to create a file-upload function in Actix web. This to save files uploaded from the client who uses something like <input type="file" name="myFile"> for example.

But I am stuck, this is what I have:

pub mod files {
    use actix_form_data::{Error, Field, Form, Value};
    use actix_multipart::Multipart;
    use actix_web::{
        web::{post, resource, Data},
        App, HttpResponse, HttpServer,
    };
    use futures::Future;
    use std::path::PathBuf;
    use uuid::Uuid;

    async fn upload(uploaded_content: Value) -> HttpResponse {
        println!("Uploaded Content: {:#?}", uploaded_content);
        HttpResponse::Created().finish()
    }
}

This is all I can find in the documentation or the internet. How would I continue from this? How to write uploaded_content to a file?

Check out the documentation for Value. You will find that it's an enum with the following cases:

pub enum Value {
    Map(HashMap<String, Value>),
    Array(Vec<Value>),
    File(FileMeta),
    Bytes(Bytes),
    Text(String),
    Int(i64),
    Float(f64),
}

You can match on this enum to extract the information you need, then do what you want with it. The enum has a few convenience methods that will let you avoid the match in some cases, and I encourage you to click the [src] button on them to learn what they do if it's not clear from their name.

2 Likes

Fully working code:

pub mod files {
    use std::io::Write;

    use actix_multipart::Multipart;
    use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer};
    use futures::{StreamExt, TryStreamExt};

    pub async fn save_file(mut payload: Multipart, file_path: String) -> Option<bool> {
        // iterate over multipart stream
        while let Ok(Some(mut field)) = payload.try_next().await {
            let content_type = field.content_disposition().unwrap();
            //let filename = content_type.get_filename().unwrap();
            let filepath = format!(".{}", file_path);

            // File::create is blocking operation, use threadpool
            let mut f = web::block(|| std::fs::File::create(filepath))
                .await
                .unwrap();

            // Field in turn is stream of *Bytes* object
            while let Some(chunk) = field.next().await {
                let data = chunk.unwrap();
                // filesystem operations are blocking, we have to use threadpool
                f = web::block(move || f.write_all(&data).map(|_| f))
                    .await
                    .unwrap();
            }
        }

        Some(true)
    }
}

Just call like this from Actix-route:

async fn route_function_example(
    mut payload: Multipart
) -> Result<HttpResponse> {
  
    let upload_status = files::save_file(payload, "/path/filename.jpg".to_string()).await;

    match upload_status {
        Some(true) => {

            Ok(HttpResponse::Ok()
                .content_type("text/plain")
                .body("update_succeeded"))
        }
        _ => Ok(HttpResponse::BadRequest()
            .content_type("text/plain")
            .body("update_failed")),
    }
}
2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.