Search code examples
rustrust-tokiorust-warp

How to write incoming stream into a file in warp?


Goal:

The server should be able to receive a stream of binary data and save it to a file.

I'm getting this error:

mismatched types

expected `&[u8]`, found type parameter `B`

How can I get a &[u8] from generic type B?

use warp::Filter;
use warp::{body};
use futures::stream::Stream;

async fn handle_upload<S, B>(stream: S) -> Result<impl warp::Reply, warp::Rejection>
where
    S: Stream<Item = Result<B, warp::Error>>,
    S: StreamExt,
    B: warp::Buf
{
    let mut file = File::create("some_binary_file").unwrap();
    
    let pinnedStream = Box::pin(stream);
    while let Some(item) = pinnedStream.next().await {
        let data = item.unwrap();
        file.write_all(data);
    }
    Ok(warp::reply())
}

#[tokio::main]
async fn main() {
    pretty_env_logger::init();

    let upload = warp::put()
    .and(warp::path("stream"))
    .and(body::stream())
    .and_then(handle_upload);
    
    warp::serve(upload).run(([127, 0, 0, 1], 3030)).await;
}

Solution

  • B implements warp::Buf which is re-exported from the bytes crate. It has a .bytes() method that returns a &[u8] which may work.

    However, the documentation says that .bytes() may return a shorter slice than what it actually contains. So you can call .bytes() and .advance() the stream while it .has_remaining() OR convert it to Bytes and send that to the file:

    let mut data = item.unwrap();
    file.write_all(data.to_bytes().as_ref());