br_oss/
lib.rs

1use std::path::PathBuf;
2use std::sync::{RwLock};
3use json::{JsonValue, object};
4use serde::{Deserialize, Serialize};
5use crate::aliyun::Aliyun;
6pub use crate::config::Config;
7use crate::tencent::Tencent;
8use once_cell::sync::Lazy;
9static GLOBAL_CONFIG: Lazy<RwLock<Config>> = Lazy::new(|| {
10    RwLock::new(Config::default())
11});
12
13mod aliyun;
14mod tencent;
15mod config;
16
17#[derive(Clone)]
18pub enum Oss {
19    Aliyun(Aliyun),
20    Tencent(Tencent),
21    None,
22}
23impl Oss {
24    /// 配置文件模式
25    pub fn new(config: Config) -> Result<Self, String> {
26        {
27            let mut data = GLOBAL_CONFIG.write().unwrap();
28            data.clone_from(&config);
29        }
30        let config = GLOBAL_CONFIG.read().unwrap();
31        let connection = config.connections.get(config.default.as_str()).unwrap().clone();
32        match connection.channel {
33            Channel::Aliyun => Ok(Oss::Aliyun(Aliyun::new(connection))),
34            Channel::Tencent => Ok(Oss::Tencent(Tencent::new(connection))),
35            Channel::None => Ok(Oss::None),
36        }
37    }
38    /// 非配置文件模式
39    pub fn create(name: &str, connection: Connection) -> Result<Self, String> {
40        {
41            let mut data = GLOBAL_CONFIG.write().unwrap();
42            if !data.connections.contains_key(name) {
43                data.connections.insert(name.to_string(), connection);
44            }
45            data.default = name.to_string();
46        }
47        let config = GLOBAL_CONFIG.read().unwrap();
48        let connection = config.connections.get(config.default.as_str()).unwrap().clone();
49        match connection.channel {
50            Channel::Aliyun => Ok(Oss::Aliyun(Aliyun::new(connection))),
51            Channel::Tencent => Ok(Oss::Tencent(Tencent::new(connection))),
52            Channel::None => Err("mode not supported!".to_string()),
53        }
54    }
55
56    pub fn connections(&mut self) -> JsonValue {
57        let mut connections = vec![];
58        let data = GLOBAL_CONFIG.read().unwrap();
59        for (item, mut value) in data.connections.clone() {
60            if value.channel.str().is_empty() {
61                continue;
62            }
63            let mut t = value.json();
64            t["name"] = item.into();
65            connections.push(t);
66        }
67        connections.into()
68    }
69}
70impl OssMode for Oss {
71    /// 推送
72    /// path 地址路径
73    fn upload(&mut self, dirname: &str, file: PathBuf, content_type: &str) -> Result<(String, String), String> {
74        let path = dirname_handle(dirname);
75        match self {
76            Oss::Aliyun(e) => e.upload(path.as_str(), file, content_type),
77            Oss::Tencent(e) => e.upload(path.as_str(), file, content_type),
78            Oss::None => Err("upload 错误".to_string())
79        }
80    }
81    fn download(&mut self, dirname: &str, key: &str) -> Result<(String, String, String, Vec<u8>), String> {
82        let path = dirname_handle(dirname);
83        match self {
84            Oss::Aliyun(e) => e.download(path.as_str(), key),
85            Oss::Tencent(e) => e.download(path.as_str(), key),
86            Oss::None => Err("download 错误".to_string())
87        }
88    }
89
90    fn query(&mut self, dirname: &str, key: &str) -> Result<(String, String), String> {
91        let path = dirname_handle(dirname);
92        match self {
93            Oss::Aliyun(e) => e.query(path.as_str(), key),
94            Oss::Tencent(e) => e.query(path.as_str(), key),
95            Oss::None => Err("query 错误".to_string())
96        }
97    }
98
99    /// 删除
100    fn del(&mut self, dirname: &str, key: &str) -> Result<bool, String> {
101        let path = dirname_handle(dirname);
102        match self {
103            Oss::Aliyun(e) => e.del(path.as_str(), key),
104            Oss::Tencent(e) => e.del(path.as_str(), key),
105            Oss::None => Err("del 错误".to_string())
106        }
107    }
108}
109fn dirname_handle(dirname: &str) -> String {
110    if dirname.is_empty() {
111        "/".to_string()
112    } else {
113        let tt = dirname.trim_start_matches("/").trim_end_matches("/").to_string();
114        format!("/{tt}/")
115    }
116}
117#[derive(Clone, Debug, Serialize, Deserialize)]
118pub struct Connection {
119    pub channel: Channel,
120    /// 腾讯云是地域信息
121    /// Endpoint以杭州为例,其它Region请按实际情况填写。 https://oss-cn-hangzhou.aliyuncs.com
122    pub endpoint: String,
123    pub access_key_id: String,
124    pub access_key_secret: String,
125    pub bucket_name: String,
126}
127
128impl Default for Connection {
129    fn default() -> Self {
130        Self {
131            channel: Channel::None,
132            endpoint: "".to_string(),
133            access_key_id: "".to_string(),
134            access_key_secret: "".to_string(),
135            bucket_name: "".to_string(),
136        }
137    }
138}
139
140impl Connection {
141    pub fn json(&mut self) -> JsonValue {
142        let mut data = object! {};
143        data["channel"] = self.channel.str().into();
144        data["endpoint"] = self.endpoint.clone().into();
145        data["access_key_id"] = self.access_key_id.clone().into();
146        data["access_key_secret"] = self.access_key_secret.clone().into();
147        data["bucket_name"] = self.bucket_name.clone().into();
148        data
149    }
150    pub fn from(data: JsonValue) -> Connection {
151        Self {
152            channel: Channel::from(data["channel"].as_str().unwrap_or("")),
153            endpoint: data["endpoint"].to_string(),
154            access_key_id: data["access_key_id"].to_string(),
155            access_key_secret: data["access_key_secret"].to_string(),
156            bucket_name: data["bucket_name"].to_string(),
157        }
158    }
159}
160
161
162#[derive(Clone, Debug, Serialize, Deserialize)]
163pub enum Channel {
164    Aliyun,
165    Tencent,
166    None,
167}
168
169impl Channel {
170    pub fn str(&mut self) -> &'static str {
171        match self {
172            Channel::Aliyun => "aliyun",
173            Channel::Tencent => "tencent",
174            Channel::None => "",
175        }
176    }
177    pub fn from(name: &str) -> Self {
178        match name {
179            "aliyun" => Channel::Aliyun,
180            "tencent" => Channel::Tencent,
181            _ => Channel::None
182        }
183    }
184}
185
186
187pub trait OssMode {
188    /// 上传
189    /// # 参数
190    /// - dirname 远程桶目录
191    /// - `file`:要上传的本地文件路径(`PathBuf`)
192    /// - `content_type`:该文件的 MIME 类型(如 `application/octet-stream`、`image/png`)
193    ///
194    /// # 返回值
195    /// - `Ok((key, url))`:上传成功,返回对应的 key(存储标识)和请求 URL(资源访问地址)
196    /// - `Err(error)`:上传失败,返回错误信息
197    fn upload(&mut self, dirname: &str, file: PathBuf, content_type: &str) -> Result<(String, String), String>;
198    /// 下载
199    /// # 返回值
200    /// - `Ok((key, url,content_type,data))`:下载成功,返回对应的 key(存储标识)和请求 URL(资源访问地址)
201    /// - `Err(error)`:下载失败,返回错误信息
202    fn download(&mut self, dirname: &str, key: &str) -> Result<(String, String, String, Vec<u8>), String>;
203    /// 查询
204    /// # 返回值
205    /// - `Ok((key, url,content_type,data))`:下载成功,返回对应的 key(存储标识)和请求 URL(资源访问地址)
206    /// - `Err(error)`:下载失败,返回错误信息
207    fn query(&mut self, dirname: &str, key: &str) -> Result<(String, String), String>;
208    /// 删除
209    fn del(&mut self, dirname: &str, key: &str) -> Result<bool, String>;
210}