aliyun_oss_rust_sdk/
url.rs

1use std::collections::HashMap;
2use reqwest::header::DATE;
3use crate::auth::AuthAPI;
4use crate::debug;
5use crate::oss::{API, OSS, OSSInfo};
6use crate::request::{RequestBuilder, RequestType};
7
8pub trait UrlApi: OSSInfo + API {
9    /// 获取签名下载URL
10    ///
11    /// # 使用例子
12    ///
13    /// ```
14    /// use aliyun_oss_rust_sdk::oss::{OSS, RequestBuilder};
15    /// use aliyun_oss_rust_sdk::url::UrlApi;
16    /// let oss = OSS::from_env();//也可以使用OSS::new()方法传递参数
17    /// let build = RequestBuilder::new()
18    ///    //.with_cdn("https://mydomain.com")
19    ///    .with_expire(60) //60秒链接过期
20    ///   .oss_download_speed_limit(30);//限速30kb
21    /// let download_url = oss.sign_download_url(
22    ///     "/ipas/cn/-10/ipadump.com_imem内存修改器_1.0.0.ipa",
23    ///     &build
24    ///     );
25    ///  println!("download_url: {}", download_url);
26    /// ```
27    fn sign_download_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String;
28
29    /// 获取签名上传URL
30    ///
31    /// # 使用例子
32    ///
33    /// ```
34    /// use aliyun_oss_rust_sdk::oss::{OSS, RequestBuilder};
35    /// use aliyun_oss_rust_sdk::url::UrlApi;
36    /// let oss = OSS::from_env();//也可以使用OSS::new()方法传递参数
37    /// let build = RequestBuilder::new()
38    ///    //.with_cdn("https://mydomain.com")
39    ///    .with_content_type("text/plain") //设置上传文件的content-type
40    ///    .with_expire(60); //60秒链接过期
41    /// let upload_url = oss.sign_upload_url(
42    ///     "tmp.txt",
43    ///     &build
44    ///     );
45    ///  println!("upload_url: {}", upload_url);
46    /// //使用postman测试上传即可,PS:要注意content-type要和build中的一致
47    /// ```
48    fn sign_upload_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String;
49    fn sign_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String;
50}
51
52impl UrlApi for OSS {
53    fn sign_download_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String {
54        let sign = self.sign_url(key.as_ref(), build);
55        if let Some(cdn) = &build.cdn {
56            let download_url = format!("{}{}", cdn, sign);
57            debug!("download_url: {}", download_url);
58            download_url
59        } else {
60            let schema = if build.https {
61                "https://"
62            } else {
63                "http://"
64            };
65            let download_url = format!("{}{}.{}{}", schema, self.bucket(), self.endpoint(), sign);
66            debug!("download_url: {}", download_url);
67            download_url
68        }
69    }
70
71    fn sign_upload_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String {
72        let mut build = build.clone();
73        build.method = RequestType::Put;
74        let sign = self.sign_url(key.as_ref(), &build);
75        if let Some(cdn) = &build.cdn {
76            let download_url = format!("{}{}", cdn, sign);
77            debug!("upload_url: {}", download_url);
78            download_url
79        } else {
80            let schema = if build.https {
81                "https://"
82            } else {
83                "http://"
84            };
85            let download_url = format!("{}{}.{}{}", schema, self.bucket(), self.endpoint(), sign);
86            debug!("upload_url: {}", download_url);
87            download_url
88        }
89    }
90
91    fn sign_url<S: AsRef<str>>(&self, key: S, build: &RequestBuilder) -> String {
92        let mut build = build.clone();
93        let key = self.format_key(key);
94        let expiration = chrono::Local::now() + chrono::Duration::seconds(build.expire);
95        build.headers.insert(DATE.to_string(), expiration.timestamp().to_string());
96        let signature = self.sign(
97            key.as_str(),
98            &build,
99        );
100        debug!("signature: {}", signature);
101        let mut query_parameters = HashMap::new();
102        query_parameters.insert("Expires".to_string(), expiration.timestamp().to_string());
103        query_parameters.insert("OSSAccessKeyId".to_string(), self.key_id().to_string());
104        query_parameters.insert("Signature".to_string(), urlencoding::encode(&signature).into_owned());
105        build.parameters.iter().for_each(|(k, v)| {
106            query_parameters.insert(k.to_string(), urlencoding::encode(v).into_owned());
107        });
108
109        let mut params = query_parameters
110            .into_iter()
111            .filter(|(k, _)| k != "x-oss-ac-source-ip")
112            .collect::<Vec<_>>();
113
114        params.sort_by(|a, b| a.0.cmp(&b.0));
115
116        format!(
117            "{}?{}",
118            self.key_urlencode(key),
119            params.into_iter().map(|(k, v)| format!("{}={}", k, v)).collect::<Vec<String>>().join("&")
120        )
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use crate::oss::OSS;
127    use crate::request::RequestBuilder;
128    use crate::url::UrlApi;
129
130    #[inline]
131    fn init_log() {
132        tracing_subscriber::fmt()
133            .with_max_level(tracing::Level::DEBUG)
134            .with_line_number(true)
135            .init();
136    }
137
138    #[test]
139    fn sign_download_url_test() {
140        init_log();
141        let oss = OSS::from_env();
142        let build = RequestBuilder::new()
143            .with_cdn("https://cdn.ipadump.com")
144            .with_expire(60)
145            .oss_download_allow_ip("14.145.28.62", 32);
146        // .oss_download_speed_limit(30);
147        oss.sign_download_url(
148            "hello.txt",
149            // "/ipas/cn/-10/ipadump.com_imem内存修改器_1.0.0.ipa",
150            &build,
151        );
152    }
153
154    #[test]
155    fn sign_upload_url_test() {
156        init_log();
157        let oss = OSS::from_env();
158        let build = RequestBuilder::new()
159            .with_cdn("http://cdn.ipadump.com")
160            .with_content_type("text/plain")
161            .with_expire(600);
162        oss.sign_upload_url(
163            "tmp.txt",
164            &build,
165        );
166    }
167}