aliyun_oss_rust_sdk/async_impl/
object.rs

1use hmac::Hmac;
2use sha1::digest::Mac;
3use crate::entity::{PolicyBuilder, PolicyResp};
4use crate::error::OssError;
5use crate::oss::{OSSInfo, API, OSS};
6use crate::request::{RequestBuilder, RequestType};
7use crate::{debug, util};
8use crate::metadata::ObjectMetadata;
9use crate::util::read_file;
10
11#[cfg(not(feature = "async"))]
12use reqwest::blocking::Client;
13#[cfg(feature = "async")]
14use reqwest::Client;
15
16#[cfg(feature = "async")]
17use maybe_async::maybe_async as maybe_async_attr;
18#[cfg(not(feature = "async"))]
19use maybe_async::must_be_sync as maybe_async_attr;
20
21impl OSS {
22    /// 获取对象
23    ///
24    /// # 使用例子
25    ///
26    /// ```rust
27    /// use aliyun_oss_rust_sdk::oss::OSS;
28    /// use aliyun_oss_rust_sdk::request::RequestBuilder;
29    /// let oss = OSS::from_env();
30    /// let build = RequestBuilder::new();
31    /// let bytes = oss.get_object("/hello.txt", build).await.unwrap();
32    /// println!("file content: {}", String::from_utf8_lossy(bytes.as_slice()));
33    /// ```
34    #[maybe_async_attr]
35    pub async fn get_object<S: AsRef<str>>(
36        &self,
37        key: S,
38        build: RequestBuilder,
39    ) -> Result<Vec<u8>, OssError> {
40        let key = self.format_key(key);
41        let (url, headers) = self
42            .build_request(key.as_str(), build)
43            .map_err(|e| OssError::Err(format!("build request error: {}", e)))?;
44        debug!("oss logget object url: {} headers: {:?}", url,headers);
45        let client = Client::new();
46        let response = client.get(url).headers(headers).send().await?;
47        return if response.status().is_success() {
48            let result = response.bytes().await?;
49            Ok(result.to_vec())
50        } else {
51            let status = response.status();
52            let result = response.text().await?;
53            debug!("oss log: get object status: {} error: {}", status,result);
54            Err(OssError::Err(format!(
55                "get object status: {} error: {}",
56                status, result
57            )))
58        };
59    }
60
61    /// 获取上传对象的policy
62    /// # 使用例子
63    /// ```rust
64    /// use aliyun_oss_rust_sdk::entity::PolicyBuilder;
65    /// use aliyun_oss_rust_sdk::oss::OSS;
66    /// let oss = OSS::from_env();
67    /// let policy_builder = PolicyBuilder::new()
68    ///             .with_expire(60 * 60)//1个小时过期
69    ///             .with_upload_dir("upload/mydir/")//上传目录
70    ///             .with_content_type("text/plain")//只允许上传文本.txt
71    ///            .with_max_upload_size(100 * 1024 * 1024);//只允许文件上传大小1G以内
72    /// let policy = oss.get_upload_object_policy(policy_builder).unwrap();
73    /// println!("policy: {:?}", policy);
74    /// //使用postman测试上传
75    /// //form-data的参数为OSSAccessKeyId、policy、signature、success_action_status、key、file
76    /// //key为上传的文件名包含路径、例如:upload/mydir/test.txt
77    /// //file为上传的文件,类型跟with_content_type一致
78    /// ```
79    pub fn get_upload_object_policy(&self, build: PolicyBuilder) -> Result<PolicyResp, OssError> {
80        let date = chrono::Local::now().naive_local() + chrono::Duration::seconds(build.expire);
81        let date_str = date.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
82        let mut json_data = r#"
83        {
84            "expiration": "{time}",
85            "conditions": [
86                {"bucket": "{bucket}" },
87                ["content-length-range", 1, {size}],
88                ["eq", "$success_action_status", "{success_action_status}"],
89                ["starts-with", "$key", "{prefix}"],
90                ["in", "$content-type", ["{content_type}"]]
91            ]
92        }
93        "#
94            .to_string();
95        let success_action_status = 200;
96        json_data = json_data.replacen("{time}", &date_str, 1);
97        json_data = json_data.replacen("{bucket}", &self.bucket(), 1);
98        //limit 1GB bytes
99        json_data = json_data.replacen("{size}", &build.max_upload_size.to_string(), 1); //允许上传的最大文件大小
100        //success status
101        json_data = json_data.replacen(
102            "{success_action_status}",
103            success_action_status.to_string().as_str(),
104            1,
105        );
106        json_data = json_data.replacen("{prefix}", &build.upload_dir, 1); //只允许上传到哪个目录上
107        //text file
108        json_data = json_data.replacen("{content_type}", &build.content_type, 1);
109        //只允许上传哪个类型文件
110        debug!("oss log: policy json: {}", json_data);
111        let base64_policy = util::base64_encode(json_data.as_bytes());
112        let mut hasher: Hmac<sha1::Sha1> = Hmac::new_from_slice(self.key_secret().as_bytes())
113            .map_err(|_| OssError::Err("Hmac new from slice error".to_string()))?;
114        hasher.update(base64_policy.as_bytes());
115        let signature = util::base64_encode(&hasher.finalize().into_bytes());
116        Ok(PolicyResp {
117            access_id: self.key_id().to_string(),
118            host: format!("https://{}.{}", self.bucket(), self.endpoint()),
119            policy: base64_policy,
120            signature,
121            success_action_status,
122        })
123    }
124
125    /// 上传文件(本地文件)
126    /// # 使用例子
127    /// ```rust
128    /// use aliyun_oss_rust_sdk::oss::OSS;
129    /// use aliyun_oss_rust_sdk::request::RequestBuilder;
130    /// let oss = OSS::from_env();
131    /// let builder = RequestBuilder::new()
132    ///     .with_expire(60);
133    /// let file_path = "./hello.txt";
134    /// oss.put_object_from_file("/hello.txt", file_path, builder).await.unwrap();
135    /// ```
136    #[maybe_async_attr]
137    pub async fn put_object_from_file<S: AsRef<str>>(
138        &self,
139        key: S,
140        file_path: S,
141        build: RequestBuilder,
142    ) -> Result<(), OssError> {
143        let buffer = read_file(file_path)?;
144        let mut build = build.clone();
145        build.method = RequestType::Put;
146        let key = self.format_key(key);
147        let (url, headers) = self
148            .build_request(key.as_str(), build)
149            .map_err(|e| OssError::Err(format!("build request error: {}", e)))?;
150        debug!("oss log: put object from file: {} headers: {:?}", url,headers);
151        let client = Client::new();
152        let response = client.put(url).headers(headers).body(buffer).send().await?;
153        if response.status().is_success() {
154            Ok(())
155        } else {
156            let status = response.status();
157            let result = response.text().await?;
158            debug!("oss log: get object status: {} error: {}", status,result);
159            Err(OssError::Err(format!(
160                "get object status: {} error: {}",
161                status, result
162            )))
163        }
164    }
165
166    /// 上传文件(内存)
167    /// # 使用例子
168    /// ```rust
169    /// use aliyun_oss_rust_sdk::oss::OSS;
170    /// use aliyun_oss_rust_sdk::request::RequestBuilder;
171    /// let oss = OSS::from_env();
172    /// let builder = RequestBuilder::new()
173    ///     .with_expire(60);
174    /// let file_path = "./hello.txt";
175    /// let buffer = std::fs::read(file_path).unwrap();
176    /// oss.pub_object_from_buffer("/hello.txt", buffer.as_slice(), builder).await.unwrap();
177    /// ```
178    #[maybe_async_attr]
179    pub async fn pub_object_from_buffer<S: AsRef<str>>(
180        &self,
181        key: S,
182        buffer: &[u8],
183        build: RequestBuilder,
184    ) -> Result<(), OssError> {
185        let mut build = build.clone();
186        build.method = RequestType::Put;
187        let key = self.format_key(key);
188        let (url, headers) = self
189            .build_request(key.as_str(), build)
190            .map_err(|e| OssError::Err(format!("build request error: {}", e)))?;
191        debug!("oss log: put object from file: {} headers: {:?}", url,headers);
192        let client = Client::new();
193        let response = client
194            .put(url)
195            .headers(headers)
196            .body(buffer.to_owned())
197            .send()
198            .await?;
199        if response.status().is_success() {
200            Ok(())
201        } else {
202            let status = response.status();
203            let result = response.text().await?;
204            debug!("oss log: get object status: {} error: {}", status,result);
205            Err(OssError::Err(format!(
206                "get object status: {} error: {}",
207                status, result
208            )))
209        }
210    }
211
212    /// 删除文件
213    /// # 使用例子
214    /// ```rust
215    /// use aliyun_oss_rust_sdk::oss::OSS;
216    /// use aliyun_oss_rust_sdk::request::RequestBuilder;
217    /// let oss = OSS::from_env();
218    /// let builder = RequestBuilder::new()
219    ///    .with_expire(60);
220    /// oss.delete_object("/hello.txt", builder).await.unwrap();
221    /// ```
222    #[maybe_async_attr]
223    pub async fn delete_object<S: AsRef<str>>(
224        &self,
225        key: S,
226        build: RequestBuilder,
227    ) -> Result<(), OssError> {
228        let mut build = build.clone();
229        build.method = RequestType::Delete;
230        let key = self.format_key(key);
231        let (url, headers) = self
232            .build_request(key.as_str(), build)
233            .map_err(|e| OssError::Err(format!("build request error: {}", e)))?;
234        debug!("oss log: put object from file: {} headers: {:?}", url,headers);
235        let client = Client::new();
236        let response = client.delete(url).headers(headers).send().await?;
237        if response.status().is_success() {
238            Ok(())
239        } else {
240            let status = response.status();
241            let result = response.text().await?;
242            debug!("oss log: get object status: {} error: {}", status,result);
243            Err(OssError::Err(format!(
244                "get object status: {} error: {}",
245                status, result
246            )))
247        }
248    }
249
250    /// 获取对象元数据
251    /// # 使用例子
252    /// ```rust
253    /// use aliyun_oss_rust_sdk::oss::OSS;
254    /// use aliyun_oss_rust_sdk::request::RequestBuilder;
255    /// let oss = OSS::from_env();
256    /// let builder = RequestBuilder::new()
257    ///    .with_expire(60);
258    /// let metadata = oss.get_object_metadata("/hello.txt", builder).await.unwrap();
259    /// println!("{:?}", metadata);
260    /// ```
261    #[maybe_async_attr]
262    pub async fn get_object_metadata<S: AsRef<str>>(&self, key: S, build: RequestBuilder) -> Result<ObjectMetadata, OssError>{
263        let mut build = build.clone();
264        build.method = RequestType::Head;
265        let key = self.format_key(key);
266        let (url, headers) = self.build_request(key.as_str(), build)
267            .map_err(|e| OssError::Err(format!("build request error: {}", e)))?;
268        debug!("put object from file: {} headers: {:?}", url,headers);
269        let client = Client::new();
270        let response = client.head(url)
271            .headers(headers)
272            .send()
273            .await?;
274        if response.status().is_success() {
275            let metadata = ObjectMetadata::new(response.headers());
276            Ok(metadata)
277        } else {
278            let status = response.status();
279            let result = response.text().await?;
280            debug!("get object status: {} error: {}", status,result);
281            Err(OssError::Err(format!("get object status: {} error: {}", status, result)))
282        }
283    }
284}
285#[cfg(test)]
286mod tests {
287    use crate::entity::PolicyBuilder;
288    use crate::oss::OSS;
289    use crate::request::RequestBuilder;
290
291    #[inline]
292    fn init_log() {
293        tracing_subscriber::fmt()
294            .with_max_level(tracing::Level::DEBUG)
295            .with_line_number(true)
296            .init();
297    }
298
299    #[test]
300    fn test_get_upload_object_policy() {
301        init_log();
302        let oss = OSS::from_env();
303        let policy_builder = PolicyBuilder::new()
304            .with_expire(60 * 60)//1个小时过期
305            .with_upload_dir("upload/mydir/")//上传目录
306            .with_content_type("text/plain")//只允许上传文本.txt
307            .with_max_upload_size(100 * 1024 * 1024);//只允许文件上传大小1G以内
308        let policy = oss.get_upload_object_policy(policy_builder).unwrap();
309        println!("policy: {:?}", policy);
310        //使用postman测试上传
311        //form-data的参数为OSSAccessKeyId、policy、signature、success_action_status、key、file
312        //key为上传的文件名包含路径、例如:upload/mydir/test.txt
313        //file为上传的文件,类型跟with_content_type一致
314    }
315
316    #[test]
317    fn test_put_object_from_file() {
318        init_log();
319        let oss = OSS::from_env();
320        let builder = RequestBuilder::new()
321            .with_expire(60);
322        let file_path = "./Cargo.toml";
323        oss.put_object_from_file("/cargo.toml", file_path, builder).unwrap();
324    }
325
326    #[test]
327    fn test_put_object_from_buffer() {
328        init_log();
329        let oss = OSS::from_env();
330        let builder = RequestBuilder::new()
331            .with_expire(60);
332        let file_path = "./Cargo.toml";
333        let buffer = std::fs::read(file_path).unwrap();
334        oss.pub_object_from_buffer("/cargo.toml", buffer.as_slice(), builder).unwrap();
335    }
336
337    #[test]
338    fn test_delete_object() {
339        init_log();
340        let oss = OSS::from_env();
341        let builder = RequestBuilder::new()
342            .with_expire(60);
343        oss.delete_object("/cargo.toml", builder).unwrap();
344    }
345
346    #[test]
347    fn test_get_object() {
348        init_log();
349        dotenvy::dotenv().ok();
350        let oss = OSS::from_env();
351        let build = RequestBuilder::new()
352            .with_cdn("http://cdn.ipadump.com");
353        let bytes = oss.get_object("/hello.txt", build).unwrap();
354        println!("file content: {}", String::from_utf8_lossy(bytes.as_slice()));
355    }
356
357    #[test]
358    fn test_get_object_metadata() {
359        init_log();
360        dotenvy::dotenv().ok();
361        let oss = OSS::from_env();
362        let build = RequestBuilder::new();
363        let metadata = oss.get_object_metadata("/hello.txt", build).unwrap();
364        println!("file metadata: {:?}", metadata);
365    }
366}