本博客是在杨旭老师的 rust web 全栈教程项目基础上进行修改,支持了图片资源返回,杨旭老师的rust web链接如下:
https://www.bilibili.com/video/BV1RP4y1G7KFp=1&vd_source=8595fbbf160cc11a0cc07cadacf22951
本人默认读者已经学习了相关基础教程和杨旭老师的相关课程,直接开始针对项目里面对应文件进行修改说明。
在原先项目加载文件方法同一层,新增支持加载图片资源如下。
pub trait Handler{
fn handle(res:&HttpRequest) ->HttpResponse;
fn load_file(file_name:&str) ->Option>{
println!("load_file file_name={}",file_name);
let default_path = format!("{}/public",env!("CARGO_MANIFEST_DIR"));
println!("load_file default_path={}",default_path);
let public_path = env::var("PUBLIC_PATH").unwrap_or(default_path);
let full_path = format!("{}/{}",public_path,file_name);
println!("load_file full_path={}",full_path);
let contents = fs::read(full_path);
contents.ok()
}
fn load_image(image_name:&str) ->Option>{
let default_path = format!("{}/public",env!("CARGO_MANIFEST_DIR"));
let public_path = env::var("PUBLIC_PATH").unwrap_or(default_path);
let full_path = format!("{}/{}/{}",public_path,"image",image_name);
println!("load_image full_path={}",full_path);
let contents: Result, std::io::Error> = fs::read(full_path);
contents.ok()
}
}
load_image 方法就是本博客新增方法,根据入参图片名称,去指定public目录下image文件夹下读取图片文件,返回Vec
我们在旧的新增了图片处理分支如下
impl Handler for StaticPageHandler{
fn handle(req:&HttpRequest) ->HttpResponse{
let http::httprequest::Resource::Path(s) =&req.resourece;
let route:Vec<&str> = s.split("/").collect();
println!("route={}",route[1]);
if route[1]=="image" {
match Self::load_image(route[2]){
Some(content) => {
let mut map:HashMap<&str,&str> = HashMap::new();
map.insert("Content-Type","image/webp");
println!("读取到文件长度{}",content.len());
return HttpResponse::new("200",Some(map),Some(content));
},
None => {
return HttpResponse::new("404",None,Self::load_file("404.html"))
}
}
}match route[1] {
"" =>HttpResponse::new("200",None,Self::load_file("index.html")),
"health" =>HttpResponse::new("200",None,Self::load_file("health.html")),
path => match Self::load_file(path) {
Some(contents) =>{
let mut map:HashMap<&str,&str> = HashMap::new();
if path.ends_with(".css") {
map.insert("Content-Type","text/css");
}else if path.ends_with(".js") {
map.insert("Content-Type","text/javascript");
}else {
map.insert("Content-Type","text/html");
}
HttpResponse::new("200",Some(map),Some(contents))
},
None => HttpResponse::new("404",None,Self::load_file("404.html"))
}
}
}
}
特别说明点根据加载图片类型不同,map.insert("Content-Type","image/webp");
这里的Content-Type也要对应修改,我这里返回的文件是webp格式。
旧项目中由于返回的都是文本内容,body数据类型定义成字符串切片,由于这次改动读取图片是字节流,所以这里的body也需要变更成Option
#[derive(Debug,PartialEq,Clone)]
pub struct HttpResponse<'a>{
version:&'a str,
status_code:&'a str,
status_text:&'a str,
header:Option>,
body:Option>,
}
上面已经提到了,旧项目返回的是文本内容,故在发送HttpResponse数据是,直接将数据转化成字符串发送。这里我们为了兼容图片资源的响应,所以body为Vec
impl <'a> From> for String{
fn from(res:HttpResponse) -> String{
let res1 = res.clone();
format!("{} {} {}\r\n{}Content-Length:{}\r\n\r\n",
&res1.version(),
&res1.status_code(),
&res1.status_text(),
&res1.header(),
&res.body.unwrap().len(),
)
}
}
相比较旧的方法,我们去掉了body参数。
上面讲过了,我们在响应转String时,将body数据拆分出来了,那么通过什么方法返回body数据。
pub fn send_response(&self, write_stream:&mut impl Write) -> Result<(),std::io::Error>{
let res = self.clone();
let response_string: String = String::from(res); // from trait
write_stream.write(response_string.as_bytes()).unwrap();
write_stream.write(&self.body()).unwrap();
write_stream.flush().unwrap();
Ok(())
}
我们就是通过write_stream.write(&self.body()).unwrap();将body数据再独立发送一下。
整理上来讲改动很小,就是将body数据类型从&str,改成了Vec