Marcus Ruddick
Marcus Ruddick

Reputation: 10395

How do I stream non-static bytes in an http response in actix_web/rust?

I am trying to generate an image and then send it in a response from actix_web, I implemented a Trace trait in which I attempted to pass the image bytes to actix_web::web::Bytes, but my attempts fail to compile because my generated image data/bytes is not static.

example code below:

pub struct Image {
  pub index: usize,
  pub bytes_in_a_chunk: usize,
  pub raw: Vec<u8>,
}

impl futures::stream::Stream for Image {
    type Item = Result<actix_web::web::Bytes, actix_web::Error>;

    fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        let length = self.raw.len();
        let bytes = self.raw.clone();
        let beg = self.index;
        let mut end = self.index + self.bytes;
        if end >= length {
            end = end - length;
        }
        let data = &bytes[beg..end];
        self.index += self.bytes_in_a_chunk;
        if data.len() <= 0 {
            Poll::Ready(None)
        } else {
            Poll::Ready(Some(Ok(actix_web::web::Bytes::from(data))))
        }
    }
}

pub async fn get_image() -> HttpResponse {
    let image: Image = generate_image();
    HttpResponse::PartialContent()
      .content_type(PNG_CONTENT_TYPE)
      .streaming(image);
}

Is there a way to stream dynamically generated bytes in a response? I assume there must be a way but I'm having a hard time finding examples or documentation that doesn't involve sending a static string.

Thanks for any help! Cheers!

Upvotes: 1

Views: 1723

Answers (1)

kmdreko
kmdreko

Reputation: 60517

Are you sure you need a stream? A stream is basically an asynchronous iterator, but if you have the full image already in memory it shouldn't be necessary.

You can copy the bytes using Body::from_slice:

pub async fn get_image() -> HttpResponse {
    let image: Image = generate_image();
    HttpResponse::Ok()
      .content_type("image/png")
      .body(Body::from_slice(&image.raw))
}

Or transfer ownership of the bytes directly:

pub async fn get_image() -> HttpResponse {
    let Image { raw, .. } = generate_image();
    HttpResponse::Ok()
      .content_type("image/png")
      .body(raw)
}

Upvotes: 3

Related Questions