use std::io::Write; use actix_multipart::{ form::{ tempfile::{TempFile, TempFileConfig}, MultipartForm, }, Multipart, }; use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer, Responder}; use futures_util::TryStreamExt as _; use uuid::Uuid; #[derive(Debug, MultipartForm)] struct UploadForm { #[multipart(rename = "file")] files: Vec, } async fn save_files( MultipartForm(form): MultipartForm, ) -> Result { for f in form.files { let path = format!("./tmp/{}", f.file_name.unwrap()); log::info!("saving to {path}"); f.file.persist(path).unwrap(); } Ok(HttpResponse::Ok()) } async fn index() -> HttpResponse { let html = r#" Upload Test
"#; HttpResponse::Ok().body(html) } #[actix_web::main] async fn main() -> std::io::Result<()> { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); log::info!("creating temporary upload directory"); std::fs::create_dir_all("./tmp")?; log::info!("starting HTTP server at http://localhost:8080"); HttpServer::new(|| { App::new() .wrap(middleware::Logger::default()) .app_data(TempFileConfig::default().directory("./tmp")) .service( web::resource("/") .route(web::get().to(index)) .route(web::post().to(save_files)), ) }) .bind(("127.0.0.1", 8080))? .workers(2) .run() .await } /// Example of the old manual way of processing multipart forms. #[allow(unused)] async fn save_file_manual(mut payload: Multipart) -> Result { // iterate over multipart stream while let Some(mut field) = payload.try_next().await? { // A multipart/form-data stream has to contain `content_disposition` let content_disposition = field.content_disposition(); let filename = content_disposition .get_filename() .map_or_else(|| Uuid::new_v4().to_string(), sanitize_filename::sanitize); let filepath = format!("./tmp/{filename}"); // File::create is blocking operation, use threadpool let mut f = web::block(|| std::fs::File::create(filepath)).await??; // Field in turn is stream of *Bytes* object while let Some(chunk) = field.try_next().await? { // filesystem operations are blocking, we have to use threadpool f = web::block(move || f.write_all(&chunk).map(|_| f)).await??; } } Ok(HttpResponse::Ok().into()) }