i try to send an image with an HttpResponse with actix web my response looks like this my problem is that i can just return an static u8 but buffer is a [u8; 4096] and not static is there any way to make it possible to send an image?
HttpResponse::Ok()
.content_type("image/jpeg")
.body(buffer)
buffer is:
let mut f = fs::File::open(x).expect("Somthing went wrong");
let mut buffer = [0;4096];
let n = f.read(&mut buffer[..]);
The full func:
fn img_response(x: PathBuf, y: Image)->HttpResponse{
let mut f = fs::File::open(x).expect("Somthing went wrong");
let mut buffer = [0;4096];
let n = f.read(&mut buffer[..]);
match y{
Image::JPG =>{
HttpResponse::Ok()
.content_type("image/jpeg")
.body(buffer)}
Image::PNG =>{
HttpResponse::Ok()
.content_type("image/png")
.body(buffer)}
Image::ICO => {
HttpResponse::Ok()
.content_type("image/x-icon")
.body(buffer)}
}
}
The func img_response get called in my index func
match path.extension().unwrap().to_str().unwrap(){
"png" => {return img_response(path, Image::PNG);}
"jpeg" => {return img_response(path, Image::JPG);}
"ico" => {return img_response(path, Image::ICO);}
};
full code: https://github.com/Benn1x/Kiwi The Code Compressed:
#![allow(non_snake_case)]
use actix_web::{ web, App, HttpRequest,HttpResponse , HttpServer};
use mime;
use std::path::PathBuf;
use serde_derive::Deserialize;
use std::process::exit;
use toml;
use std::fs::read_to_string;
use actix_web::http::header::ContentType;
use std::fs;
use std::io::prelude::*;
use std::io;
fn img_response(x: PathBuf)->HttpResponse{
let mut f = fs::File::open(x).expect("Somthing went wrong");
let mut buffer = [0;4096];
let n = f.read(&mut buffer[..]);
HttpResponse::Ok()
.content_type("image/jpeg")
.body(buffer)
}
async fn index(req: HttpRequest) -> HttpResponse {
let mut path: PathBuf = req.match_info().query("file").parse().unwrap();
match path.extension().unwrap().to_str().unwrap(){
"jpeg" => {return img_response(path);}
_ => {return img_response(path);}
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
App::new()
.route("/{file:.*}", web::get().to(index))
.service(actix_files::Files::new("/", ".").index_file("index.html"))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
This is the The main.rs but just with the func that returns an image
HttpResponse::Ok()
returns a HttpResponseBuilder
.
Its method .body()
takes a generic argument that has to implement the MessageBody
trait.
Now here is your problem: [u8; 4096]
does not implement MessageBody
. What does, however, implement MessageBody
, is Vec<u8>
.
Therefore by modifying your static array to a dynamic vector, your code seems to compile:
#![allow(non_snake_case)]
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
use std::fs;
use std::io::prelude::*;
use std::path::PathBuf;
fn img_response(x: PathBuf) -> HttpResponse {
let mut f = fs::File::open(x).expect("Somthing went wrong");
let mut buffer = vec![0; 4096];
let n = f.read(&mut buffer[..]);
HttpResponse::Ok().content_type("image/jpeg").body(buffer)
}
async fn index(req: HttpRequest) -> HttpResponse {
let mut path: PathBuf = req.match_info().query("file").parse().unwrap();
match path.extension().unwrap().to_str().unwrap() {
"jpeg" => {
return img_response(path);
}
_ => {
return img_response(path);
}
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
App::new()
.route("/{file:.*}", web::get().to(index))
.service(actix_files::Files::new("/", ".").index_file("index.html"))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
There are still problems with your code, though:
Here is a working version of your code:
#![allow(non_snake_case)]
use actix_web::{web, App, HttpRequest, HttpResponse, HttpServer};
use std::fs;
use std::io::prelude::*;
use std::path::PathBuf;
fn img_response(x: PathBuf) -> HttpResponse {
let mut f = fs::File::open(x).expect("Somthing went wrong");
let mut image_data = vec![];
let mut buffer = [0; 4096];
loop {
let n = f.read(&mut buffer[..]).unwrap();
if n == 0 {
break;
}
image_data.extend_from_slice(&buffer[..n]);
}
HttpResponse::Ok()
.content_type("image/jpeg")
.body(image_data)
}
async fn index(req: HttpRequest) -> HttpResponse {
let path: PathBuf = req.match_info().query("file").parse().unwrap();
match path.extension().unwrap().to_str().unwrap() {
"jpeg" => {
return img_response(path);
}
_ => {
return img_response(path);
}
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
App::new()
.route("/{file:.*}", web::get().to(index))
.service(actix_files::Files::new("/", ".").index_file("index.html"))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Note that for large image sizes, it would be benefitial to use the .streaming()
body instead:
#![allow(non_snake_case)]
use actix_web::{web, App, Error, HttpRequest, HttpResponse, HttpServer};
use async_stream::{try_stream, AsyncStream};
use bytes::Bytes;
use std::fs;
use std::io::prelude::*;
use std::path::PathBuf;
fn img_response(x: PathBuf) -> HttpResponse {
let stream: AsyncStream<Result<Bytes, Error>, _> = try_stream! {
let mut f = fs::File::open(x)?;
let mut buffer = [0; 4096];
loop{
let n = f.read(&mut buffer[..])?;
if n == 0 {
break;
}
yield Bytes::copy_from_slice(&buffer[..n]);
}
};
HttpResponse::Ok()
.content_type("image/jpeg")
.streaming(stream)
}
async fn index(req: HttpRequest) -> HttpResponse {
let path: PathBuf = req.match_info().query("file").parse().unwrap();
match path.extension().unwrap().to_str().unwrap() {
"jpeg" => {
return img_response(path);
}
_ => {
return img_response(path);
}
}
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
App::new()
.route("/{file:.*}", web::get().to(index))
.service(actix_files::Files::new("/", ".").index_file("index.html"))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}