0

I want to send image to the Frontend with a path /images/{pic_name}. Here is the actix web way

.service(web::resource("/images/{pic_name}").route(web::get().to(images)))

And here the fuction images:

async fn images(req: HttpRequest, info: web::Path<Info>) -> Result<HttpResponse, Error> {
      Ok(HttpResponse::build(StatusCode::OK)
            .content_type("image/jpeg")
            .body("images/".to_string() + &info.pic_name + &".jpeg"))
}

The Folder structure is:

images     -| -> inside jpeg and webp
src        -| -> inside the source files rs
Cargo.toml -|

My Cargo toml is

[dependencies]
actix-web = "4.0.0-beta.5"
actix-service = "2.0.0-beta.5"
actix-files = "0.6.0-beta.8"
actix-rt = "2.4.0"

Question: What is the way to send an image like the nodejs on express sendFile way,

res.sendFile(path.join(__dirname, `/assets/images/${req.params.picName}.${acceptedHeader}`));

Edit: maybe the path is wrong or something, cause i get a 200 and always the image is broken.

Klod Lala
  • 153
  • 8
  • 2
    _"I get a 200 and always the image is broken"_ That's because you are sending the file path as the actual response, rather than the contents of that file. – E_net4 Nov 23 '21 at 10:07
  • new to Rust here, how could i send the content? Found no basic example about that. – Klod Lala Nov 23 '21 at 10:16

4 Answers4

2

As mentioned, you are sending the path instead of the file content. First, you should read the content by doing something like this:

let image_content = web::block(|| std::fs::read(<your path>)).await?;

block "execute blocking function on a thread pool, returns future that resolves to result of the function execution." It's needed because -- as far as I know -- std::fs::read is blocking.

Then, if you have the content, return it:

Ok(HttpResponse::build(StatusCode::OK)
            .content_type("image/jpeg")
            .body(image_content))
E_net4
  • 27,810
  • 13
  • 101
  • 139
Riwen
  • 4,734
  • 2
  • 19
  • 31
  • your answer is correct, but on actix-web 4 beta there is an error: the trait `std::convert::From, std::io::Error>>` is not implemented for `AnyBody`. I will try to fix that and then check your answer. Thanks – Klod Lala Nov 23 '21 at 14:06
2

That response is only sending the file path as the body. You would need to open and read the file to retrieve its contents first, either via standard std::fs::read, tokio_fs::read, or something else. See this answer for a possible solution to this.

But with actix-files, and assuming that no additional logic is needed, you only need to set up a route to a new static file service.

use actix_web::App;
use actix_files::Files;

let app = App::new()
    .service(Files::new("/images", "./images"));
E_net4
  • 27,810
  • 13
  • 101
  • 139
0

The solution from @Riwen is the correct one, but i was using actix-web 4 beta, so it had errors cause of the changes, solution i used is:

let image_content =  actix_web::web::Bytes::from(std::fs::read("src/images/".to_string() + &info.pic_name + ".jpeg")?);
Ok(HttpResponse::build(StatusCode::OK)
.content_type("image/jpeg")
.body(actix_web::dev::AnyBody::Bytes(image_content)))

Thas seems to work, but i am new to Rust and no idea if it is a good practice. P.S. I am not using blocking like the answer from @Riwen, but still the images are appearing on the frontend. I will comment for more info. Thanks

Klod Lala
  • 153
  • 8
0

The examples above read the data into memory then return the response. For small files that may be OK, however, you should consider using a streaming response instead of something resident in memory. Something like what is demonstrated here using the actix_files crate:


async fn get_file(filename: web::Path) -> HttpResponse {
    let file_path = std::path::PathBuf::from(env!("HOME"))
        .as_path()
        .join(BASE_DIR)
        .join(DATA_PATH)
        .join(&filename.into_inner());
    
    let file = actix_files::NamedFile::open_async(file_path).await.unwrap();

    file.into_response()
}

https://github.com/actix/actix-web/discussions/2720#discussioncomment-2510752

Chris Andrew
  • 103
  • 6