Search code examples
rustmultipartrust-warp

How can I post and save a photo using Rust Warp Multipart?


Rust warp multipart. How to post photo with warp and then save it as file?

I've tried this code:

pub async fn photos(form: warp::multipart::FormData) -> Result<impl Reply, Rejection> {
    let parts: Vec<Part> = form.try_collect().await.map_err(|e| {
        eprintln!("form error: {}", e);
        warp::reject::reject()
    })?;

    for p in parts {
        eprintln!("{:#?}", p);

        let filename = p.filename().unwrap_or("photo.png");
        let filepath = format!("uploads/{}", filename);

        fs::create_dir_all("uploads").unwrap();
        let mut file = fs::File::create(&filepath).unwrap();

        let data = p.stream().next().await;
        file.write_all(data.unwrap().unwrap().chunk()).unwrap();
    }
    /* 

    while let Some(chunk) = photo.stream().try_next().await? {
        file.write_all(&chunk.chunk()).unwrap();
    } */

    Ok("Upload successful!")
}

but when I send photo is says: form error: failed to lock multipart state


Solution

  • Here is solution that i came to:

    pub async fn photos(form: warp::multipart::FormData) -> Result<impl Reply, Rejection> {
        task::spawn(async move {
            let mut parts = form.into_stream();
    
            while let Ok(p) = parts.next().await.unwrap() {
                let filename = p.filename().unwrap_or("photo.png");
                let filepath = format!("uploads/{}", filename);
    
                fs::create_dir_all("uploads").unwrap();
    
                save_part_to_file(&filepath, p).await.expect("save error");
            }
        });
    
        Ok("Upload successful!")
    }
    
    async fn save_part_to_file(path: &str, part: warp::multipart::Part) -> Result<(), std::io::Error> {
        let data = part
            .stream()
            .try_fold(Vec::new(), |mut acc, buf| async move {
                acc.extend_from_slice(buf.chunk());
                Ok(acc)
            })
            .await.expect("folding error");
        std::fs::write(path, data)
    }