Skip to main content

wae_storage/providers/
oss.rs

1//! 阿里云 OSS 存储提供商实现
2
3use crate::{StorageConfig, StorageProvider, StorageResult};
4
5use base64::{Engine as _, engine::general_purpose::STANDARD};
6use chrono::Utc;
7use hmac::{Hmac, Mac};
8use sha1::Sha1;
9use url::Url;
10use wae_types::WaeError;
11
12/// 阿里云 OSS 存储提供商
13///
14/// 实现阿里云对象存储服务的签名和预签名 URL 生成
15pub struct OssProvider;
16
17impl StorageProvider for OssProvider {
18    fn sign_url(&self, path: &str, config: &StorageConfig) -> StorageResult<Url> {
19        if path.is_empty() {
20            return Err(WaeError::invalid_params("path", "Empty path"));
21        }
22
23        if path.starts_with("http://") || path.starts_with("https://") {
24            return Url::parse(path).map_err(|e| WaeError::invalid_params("path", e.to_string()));
25        }
26
27        let clean_path = path.trim_start_matches('/');
28        let expires = Utc::now().timestamp() + 3600;
29
30        let host_str =
31            config.cdn_url.clone().unwrap_or_else(|| format!("https://{}.{}.aliyuncs.com", config.bucket, config.region));
32
33        let host_url =
34            if host_str.starts_with("http") { Url::parse(&host_str) } else { Url::parse(&format!("https://{}", host_str)) }
35                .map_err(|e| WaeError::invalid_params("host", e.to_string()))?;
36
37        let mut url = host_url.join(clean_path).map_err(|e| WaeError::invalid_params("path", e.to_string()))?;
38
39        let string_to_sign = format!("GET\n\n\n{}\n/{}/{}", expires, config.bucket, clean_path);
40
41        let mut mac =
42            Hmac::<Sha1>::new_from_slice(config.secret_key.as_bytes()).map_err(|e| WaeError::internal(e.to_string()))?;
43        mac.update(string_to_sign.as_bytes());
44        let signature = STANDARD.encode(mac.finalize().into_bytes());
45
46        url.query_pairs_mut()
47            .append_pair("OSSAccessKeyId", &config.secret_id)
48            .append_pair("Expires", &expires.to_string())
49            .append_pair("Signature", &signature);
50
51        Ok(url)
52    }
53
54    fn get_presigned_put_url(&self, key: &str, config: &StorageConfig) -> StorageResult<Url> {
55        let clean_key = key.trim_start_matches('/');
56        let expires = Utc::now().timestamp() + 900;
57
58        let host_str =
59            config.endpoint.clone().unwrap_or_else(|| format!("https://{}.{}.aliyuncs.com", config.bucket, config.region));
60
61        let host_url =
62            if host_str.starts_with("http") { Url::parse(&host_str) } else { Url::parse(&format!("https://{}", host_str)) }
63                .map_err(|e| WaeError::invalid_params("host", e.to_string()))?;
64
65        let mut url = host_url.join(clean_key).map_err(|e| WaeError::invalid_params("key", e.to_string()))?;
66
67        let string_to_sign = format!("PUT\n\n\n{}\n/{}/{}", expires, config.bucket, clean_key);
68
69        let mut mac =
70            Hmac::<Sha1>::new_from_slice(config.secret_key.as_bytes()).map_err(|e| WaeError::internal(e.to_string()))?;
71        mac.update(string_to_sign.as_bytes());
72        let signature = STANDARD.encode(mac.finalize().into_bytes());
73
74        url.query_pairs_mut()
75            .append_pair("OSSAccessKeyId", &config.secret_id)
76            .append_pair("Expires", &expires.to_string())
77            .append_pair("Signature", &signature);
78
79        Ok(url)
80    }
81}