rust 使用HttpResponse发送图像ActixWeb

fnvucqvd  于 2023-10-20  发布在  其他
关注(0)|答案(2)|浏览(142)

我尝试用actix web发送一个带有HttpResponse的图像,我的响应看起来像这样,我的问题是我只能返回一个静态u8,但缓冲区是一个[u8; 4096]而不是静态的,有没有什么方法可以使发送图像成为可能?

HttpResponse::Ok()
           .content_type("image/jpeg")
           .body(buffer)

buffer是:

let mut f = fs::File::open(x).expect("Somthing went wrong");
let mut buffer = [0;4096];
let n = f.read(&mut buffer[..]);

完整功能:

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)}
        }   
}

在我的索引函数中调用函数img_response

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);}
};

完整代码:https://github.com/Benn1x/Kiwi代码压缩:

#![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
}

这是main.rs,但只是返回图像的func

oprakyz7

oprakyz71#

HttpResponse::Ok()返回HttpResponseBuilder。它的方法.body()接受一个泛型参数,该参数必须实现MessageBody trait。
你的问题是[u8; 4096]不实现MessageBody。但是,**实现MessageBody的是Vec<u8>
因此,通过将静态数组修改为动态向量,您的代码似乎可以编译:

#![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
}

但是,你的代码仍然存在一些问题:

  • 缓冲区没有被切片到正确的大小
  • 大于缓冲区的图像将被缩减到前4096个字节

下面是你的代码的工作版本:

#![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
}

请注意,对于较大的图像尺寸,使用.streaming()主体将是有益的:

#![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
}
a11xaf1n

a11xaf1n2#

作为替代:

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 img_response(x: PathBuf) -> HttpResponse {
    let file = actix_files::NamedFile::open_async(x).await.unwrap();
    file.into_response()
}

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

相关问题