rocket_gitee_pictures/
lib.rs

1use deadpool_redis::Pool;
2
3mod redis_pool_wrapper;
4use anyhow;
5use base64;
6use deadpool_redis::{cmd, Pipeline};
7use mime_sniffer::MimeTypeSniffer;
8use reqwest;
9use rocket::data::{Data, ToByteUnit};
10use rocket::http::{
11    ContentType,
12    Method::{Get, Post},
13    Status,
14};
15use rocket::outcome::{try_outcome, IntoOutcome};
16use rocket::response::{self, status::Custom, Responder};
17use rocket::tokio::fs::File;
18use rocket::{catcher, route, Catcher, Request, Response, Route};
19use serde_json;
20use std::cell::RefCell;
21use std::error::Error;
22use std::io::Cursor;
23use std::path::Path;
24use std::path::PathBuf;
25use std::rc::Rc;
26use std::sync::{Arc, Mutex};
27use std::thread;
28//配置
29#[derive(Clone)]
30pub struct RocketGiteePicturesWrapperConfig {
31    //redis 主机IP
32    pub redis_host: String,
33    //redis端口
34    pub redis_port: i32,
35    //redis 主机密码
36    pub redis_password: String,
37    //gitee token
38    pub gitee_token: String,
39    //gitee账号
40    pub gitee_owner: String,
41    //仓库名
42    pub gitee_repo: String,
43    //仓库分支名
44    pub gitee_branch: String,
45    //文件缓存刷新间隔
46    pub gitee_refresh_interval: i32,
47}
48
49//图C用自定义route handler
50#[derive(Clone)]
51pub struct RocketGiteePicturesHandler {
52    //handler配置
53    pub config: RocketGiteePicturesWrapperConfig,
54    //redis线程池
55    pub redis_pool: Pool,
56}
57
58//加载gitee文件路径到 redis缓存
59pub async fn load_git_files_path_to_redis(
60    config: &RocketGiteePicturesWrapperConfig,
61    pool: &Pool,
62) -> Result<(), anyhow::Error> {
63    //获取文件列表
64    let body = reqwest::get(format!(
65        "https://gitee.com/api/v5/repos/{}/{}/git/trees/{}?access_token={}&recursive=1",
66        &config.gitee_owner, &config.gitee_repo, &config.gitee_branch, &config.gitee_token
67    ))
68    .await?
69    .text()
70    .await?;
71
72    //分解结果json
73    let v: serde_json::Value = serde_json::from_str(&body)?;
74    //文件列表
75    let tree = v.get("tree");
76    match (tree) {
77        Some(fs) => {
78            let filearray = fs.as_array();
79
80            match (filearray) {
81                Some(files) => {
82                    //获取连接
83                    let mut redisconn = pool.get().await?;
84                    let mut pipeline = Pipeline::new();
85                    for file in files {
86                        //文件路径
87                        let filepath = file.get("path").unwrap().as_str().unwrap();
88                        //文件链接
89                        let fileurl = file.get("url").unwrap().as_str().unwrap();
90
91                        //缓存链接
92                        pipeline.cmd("HSET").arg(&[
93                            config.gitee_repo.to_owned(),
94                            filepath.to_owned(),
95                            fileurl.to_owned(),
96                        ]); 
97                    }
98                    pipeline.execute_async(&mut redisconn).await?;
99                }
100                None => {}
101            }
102        }
103        None => {}
104    }
105
106    Ok(())
107}
108
109//图C用自定义route handler
110impl RocketGiteePicturesHandler {
111    pub async fn routes(
112        path: &'static str,
113        config: RocketGiteePicturesWrapperConfig,
114    ) -> Vec<Route> {
115        let task_config = config.clone();
116
117        rocket::tokio::spawn(async move {
118            println!("加载gitee文件路径到 redis缓存..");
119            //获取连接池
120            let task_pool = redis_pool_wrapper::get_pool(
121                &task_config.redis_password,
122                &task_config.redis_host,
123                &task_config.redis_port,
124            );
125            //加载gitee文件路径到 redis缓存
126            loop {
127                load_git_files_path_to_redis(&task_config, &task_pool)
128                    .await
129                    .unwrap();
130
131                rocket::tokio::time::sleep(std::time::Duration::from_millis(
132                    task_config.gitee_refresh_interval as u64,
133                ))
134                .await;
135            }
136        });
137
138        //获取连接池
139        let web_pool = redis_pool_wrapper::get_pool(
140            &config.redis_password,
141            &config.redis_host,
142            &config.redis_port,
143        );
144
145        let handler = Self {
146            config: config.clone(),
147            //多线程可变字段  RefCell,多线程加  Mutex, Clone 加 Arc
148            redis_pool: web_pool,
149        };
150        //传递配置参数
151        vec![Route::new(Get, &path, handler)]
152    }
153}
154
155#[rocket::async_trait]
156impl route::Handler for RocketGiteePicturesHandler {
157    async fn handle<'r>(&self, req: &'r Request<'_>, data: Data<'r>) -> route::Outcome<'r> {
158        //获取图片路径
159        let image_path = req
160            .segments::<PathBuf>(0..)
161            .unwrap()
162            .to_str()
163            .unwrap()
164            .replace("\\", "/");
165
166        println!("image_path={}", &image_path);
167
168        //redis 连接池
169        let redis_pool = &self.redis_pool;
170        //获取连接
171        let mut redisconn = redis_pool.get().await.unwrap();
172        let v = cmd("HGET")
173            .arg(&[self.config.gitee_repo.to_owned(), image_path.to_owned()])
174            .query_async::<String>(&mut redisconn)
175            .await;
176        //文件链接
177        let mut file_url: String = String::from("");
178        match v {
179            Ok(s) => {
180                file_url = s.clone();
181            }
182            Err(_e) => {}
183        }
184
185        if file_url == "" {
186            return route::Outcome::from(req, "");
187        }
188
189        //根据链接获取图片数据
190        let response_body = reqwest::get(format!(
191            "{}?access_token={}",
192            &file_url, &self.config.gitee_token
193        ))
194        .await
195        .unwrap()
196        .text()
197        .await
198        .unwrap();
199
200        let response_body_json: serde_json::Value = serde_json::from_str(&response_body).unwrap();
201        let response_body_content = response_body_json.get("content").unwrap().as_str().unwrap();
202        let response_body_bytes = base64::decode(response_body_content).unwrap();
203        println!("file_url={}", &file_url);
204
205        route::Outcome::from(
206            req,
207            (
208                ContentType::parse_flexible("image/webp").unwrap(),
209                response_body_bytes,
210            ),
211        )
212    }
213}