tonyd629
tonyd629

Reputation: 117

How to read full multipart stream in Warp?

I am trying to build a simple web server to upload and save images using Rust. Here is my code:

use std::convert::Infallible;
use std::path::PathBuf;
use warp::multipart::FormData;
use warp::{http::StatusCode, Filter};
use futures::stream::TryStreamExt;

#[tokio::main]
async fn main() {
    let upload_route = warp::path("upload")
        .and(warp::post())
        .and(warp::multipart::form().max_length(3 * 1024 * 1024))
        .and_then(upload_handler);

    let hello_route = warp::path!("hello")
        .and(warp::get())
        .and(warp::fs::file("./static/index.html"));

    let routes = upload_route.or(hello_route);

    println!("Server started at http://localhost:8080");
    warp::serve(routes).run(([127, 0, 0, 1], 8080)).await;
}

async fn upload_handler(mut form: FormData) -> Result<impl warp::Reply, Infallible> {
    while let Ok(Some(part)) = form.try_next().await {
        let filename = match part.filename() {
            Some(filename) => filename.to_string(),
            None => return Ok(StatusCode::BAD_REQUEST),
        };
        println!("Receiving file: {}", filename);

        let mut data = Vec::new();
        let mut stream = part.stream();
        
        while let Ok(Some(chunk)) = stream.try_next().await {
            data.extend_from_slice(&chunk);
        }

        let save_path = PathBuf::from(format!("./uploads/{}", filename));
        if let Err(_) = tokio::fs::write(save_path, data).await {
            return Ok(StatusCode::INTERNAL_SERVER_ERROR);
        }
    }
    Ok(StatusCode::CREATED)
}

My problem is in line data.extend_from_slice(&chunk);:

expected `&[_]`, found `&impl Buf`

I am not exactly sure what [_] means and why data.extend_from_slice does not accept the chunk bytes.

Upvotes: 0

Views: 437

Answers (2)

drewtato
drewtato

Reputation: 12812

You can use reader to create an implementer of Read, and then call read_to_end.

while let Ok(Some(mut chunk)) = stream.try_next().await {
    chunk.reader().read_to_end(&mut data).unwrap();
}

The issue with chunk is that it doesn't have to return the entire buffer.

Note that this can return shorter slice

If you want to use chunk, you have to loop until remaining is zero.

while let Ok(Some(mut chunk)) = stream.try_next().await {
    while chunk.remaining() > 0 {
        let slice: &[u8] = chunk.chunk();
        data.extend_from_slice(slice);
        chunk.advance(slice.len());
    }
}

Upvotes: 1

tonyd629
tonyd629

Reputation: 117

I was able to address this type error using the Buf chunk method like so

while let Ok(Some(mut chunk)) = stream.try_next().await {
    let slice: &[u8] = chunk.chunk();
    data.extend_from_slice(slice);
    chunk.advance(slice.len());
}

Upvotes: 0

Related Questions