Search code examples
imagerestrustrust-axum

Send a DynamicImage as a response with axum


I tried to send a PNG as GET-response to a client with axum. For this I load an image into a DynamicImage from the image crate. When I send a request from my browser, no image is shown. In firefox I get the message:

The image "http://<the-URL-of-the-GET-request>" cannot be displayed because it contains errors.

In my terminal is no error message.

The reason I put the image in an DynamicImage object in my example is because I want to generate images on request and then reply to the client.


What I've tried

First, I created an API, where I can get the image following the instructions in the axum documentation.

let app = Router::new().route("/image", get(get_image)).with_state(());
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
    .serve(app.into_make_service())
    .await
    .unwrap();
}

The function get_image() loads the PNG-file, put it in an DynamicImage from the image crate. Next, I try to send the bytes back to the client.

pub async fn get_image() -> impl axum::response::IntoResponse {
    let img_path = PathBuf::from("<path/to/image>").join("ferris.png");
    let image = image::io::Reader::open(&img_path).unwrap().decode().unwrap();

    (
        axum::response::AppendHeaders([(header::CONTENT_TYPE, "image/png")]),
        image.into_bytes()
    )
}

For this, I adapted the code from here. But I tried to load the image from hard drive and not, as in the example shown, from base64 string.

I also converted the image to a base64 string and sent this back as shown in the example. This worked fine. However, I don't want to convert the image into a string every time, only to be able to send it again as a byte stream. Is there any direct way that I am overlooking?



Solution

  • Instead of converting the raw bitmap (your DynamicImage) into bytes, you must encode it to adhere to the PNG format.

    Something like this should work to convert the raw image to PNG and write the PNG to a a byte buffer that you can serve with axum:

    use image::ImageFormat;
    
    use std::io::{BufWriter, Cursor};
    
    let mut buffer = BufWriter::new(Cursor::new(Vec::new()));
    image.write_to(&mut buffer, ImageFormat::Png).unwrap();
        
    let bytes: Vec<u8> = buffer.into_inner().unwrap().into_inner();