xt_oss/oss/api/objects/
stand.rs

1use builders::{DeleteObjectBuilder, GetObjectBuilder, PutObjectBuilder};
2
3use crate::oss::{self, api::objects::stand::builders::GetObjectMetaBuilder};
4
5use self::builders::{
6    AppendObjectBuilder, CopyObjectBuilder, DeleteMultipleObjectsBuilder, HeadObjectBuilder,
7    RestoreObjectBuilder,
8};
9
10pub mod builders {
11
12    use std::collections::HashMap;
13
14    use chrono::{DateTime, Utc};
15    use oss::http::{
16        header::{
17            HeaderMap, ACCEPT_ENCODING, CACHE_CONTROL, CONTENT_DISPOSITION, CONTENT_ENCODING,
18            CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_TYPE, ETAG, EXPIRES, IF_MATCH,
19            IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_UNMODIFIED_SINCE, RANGE,
20        },
21        CacheControl, ContentDisposition, ContentEncoding,
22    };
23    use reqwest::Response;
24    use serde::{Deserialize, Serialize};
25
26    use crate::{oss::entities::object::delete_multiple::DeleteResult, util::ByteRange};
27    use crate::{
28        oss::{
29            self,
30            api::{self, insert_custom_header, insert_header, ApiResponseFrom},
31            entities::{
32                object::{
33                    delete_multiple::{Delete, Object},
34                    CopyObjectResult, JobParameters, MetadataDirective, RestoreRequest,
35                    TaggingDirective, Tier,
36                },
37                ObjectACL, ServerSideEncryption, StorageClass,
38            },
39            http, Bytes,
40        },
41        util::oss_md5,
42    };
43
44    #[derive(Debug, Default)]
45    struct PutObjectBuilderHeaders<'a> {
46        content_type: Option<String>,
47        content_encoding: Option<http::ContentEncoding>,
48        content_language: Option<String>,
49        content_disposition: Option<http::ContentDisposition>,
50        cache_control: Option<http::CacheControl>,
51        expires: Option<&'a str>,
52        content_length: Option<u64>,
53        content_md5: Option<String>,
54        etag: Option<String>,
55        forbid_overwrite: Option<bool>,
56        encryption: Option<ServerSideEncryption>,
57        data_encryption: Option<String>,
58        encryption_key_id: Option<String>,
59        object_acl: Option<ObjectACL>,
60        storage_class: Option<StorageClass>,
61        oss_tagging: Option<Vec<(&'a str, &'a str)>>,
62        // oss_meta:  HashMap<String, String>,
63        oss_meta: Option<Vec<(&'a str, &'a str)>>,
64    }
65
66    pub struct PutObjectBuilder<'a> {
67        client: &'a oss::Client<'a>,
68        object: &'a str,
69        content: oss::Bytes,
70        headers: PutObjectBuilderHeaders<'a>,
71        timeout: Option<u64>,
72    }
73
74    impl<'a> PutObjectBuilder<'a> {
75        pub(crate) fn new(client: &'a oss::Client, object: &'a str) -> Self {
76            Self {
77                client,
78                object,
79                content: oss::Bytes::new(),
80                headers: PutObjectBuilderHeaders::default(),
81                timeout: None,
82            }
83        }
84
85        pub fn with_content_type(mut self, value: &'a str) -> Self {
86            self.headers.content_type = Some(value.to_string());
87            self
88        }
89
90        pub fn with_content_language(mut self, value: &'a str) -> Self {
91            self.headers.content_language = Some(value.to_string());
92            self
93        }
94
95        pub fn with_cache_control(mut self, value: http::CacheControl) -> Self {
96            self.headers.cache_control = Some(value);
97            self
98        }
99
100        pub fn with_content_disposition(mut self, value: http::ContentDisposition) -> Self {
101            self.headers.content_disposition = Some(value);
102            self
103        }
104
105        pub fn with_content_encoding(mut self, value: http::ContentEncoding) -> Self {
106            self.headers.content_encoding = Some(value);
107            self
108        }
109
110        pub fn with_content_md5(mut self, value: &'a str) -> Self {
111            self.headers.content_md5 = Some(value.to_string());
112            self
113        }
114
115        pub fn with_content_length(mut self, value: u64) -> Self {
116            self.headers.content_length = Some(value);
117            self
118        }
119
120        pub fn with_etag(mut self, value: &'a str) -> Self {
121            self.headers.etag = Some(value.to_string());
122            self
123        }
124
125        pub fn with_expires(mut self, value: &'a str) -> Self {
126            self.headers.expires = Some(value);
127            self
128        }
129
130        pub fn with_forbid_overwrite(mut self, value: bool) -> Self {
131            self.headers.forbid_overwrite = Some(value);
132            self
133        }
134
135        pub fn with_encryption(mut self, value: ServerSideEncryption) -> Self {
136            self.headers.encryption = Some(value);
137            self
138        }
139
140        pub fn with_data_encryption(mut self, value: &'a str) -> Self {
141            self.headers.data_encryption = Some(value.to_string());
142            self
143        }
144
145        pub fn with_encryption_key_id(mut self, value: &'a str) -> Self {
146            self.headers.encryption_key_id = Some(value.to_string());
147            self
148        }
149
150        pub fn with_object_acl(mut self, value: ObjectACL) -> Self {
151            self.headers.object_acl = Some(value);
152            self
153        }
154
155        pub fn with_storage_class(mut self, value: StorageClass) -> Self {
156            self.headers.storage_class = Some(value);
157            self
158        }
159
160        pub fn with_oss_tagging(mut self, value: Vec<(&'a str, &'a str)>) -> Self {
161            self.headers.oss_tagging = Some(value);
162            self
163        }
164
165        pub fn with_oss_meta(mut self, value: Vec<(&'a str, &'a str)>) -> Self {
166            self.headers.oss_meta = Some(value);
167            self
168        }
169
170        pub fn with_content(mut self, content: oss::Bytes) -> Self {
171            self.content = content;
172            self
173        }
174
175        pub fn with_timeout(mut self, value: u64) -> Self {
176            self.timeout = Some(value);
177            self
178        }
179
180        fn headers(&self) -> http::HeaderMap {
181            let mut headers = http::HeaderMap::new();
182
183            if let Some(content_type) = &self.headers.content_type {
184                insert_header(&mut headers, CONTENT_TYPE, content_type);
185            }
186
187            if let Some(content_language) = &self.headers.content_language {
188                insert_header(&mut headers, CONTENT_LANGUAGE, content_language);
189            }
190            if let Some(content_type) = &self.headers.content_type {
191                insert_header(&mut headers, CONTENT_TYPE, content_type);
192            }
193
194            if let Some(cache_control) = &self.headers.cache_control {
195                insert_header(&mut headers, CACHE_CONTROL, cache_control);
196            }
197
198            if let Some(content_disposition) = &self.headers.content_disposition {
199                insert_header(&mut headers, CONTENT_DISPOSITION, content_disposition);
200            }
201
202            if let Some(content_encoding) = &self.headers.content_encoding {
203                insert_header(&mut headers, CONTENT_ENCODING, content_encoding);
204            }
205
206            if let Some(content_length) = &self.headers.content_length {
207                insert_header(&mut headers, CONTENT_LENGTH, content_length);
208            }
209
210            if let Some(etag) = &self.headers.etag {
211                insert_header(&mut headers, ETAG, etag);
212            }
213
214            if let Some(content_md5) = &self.headers.content_md5 {
215                headers.insert("Content-MD5", content_md5.parse().unwrap());
216            }
217
218            if let Some(expires) = &self.headers.expires {
219                insert_header(&mut headers, EXPIRES, expires);
220            }
221
222            if let Some(forbid_overwrite) = &self.headers.forbid_overwrite {
223                insert_custom_header(&mut headers, "x-oss-forbid-overwrite", forbid_overwrite);
224            }
225
226            if let Some(encryption) = &self.headers.encryption {
227                insert_custom_header(&mut headers, "x-oss-server-side-encryption", encryption);
228            }
229
230            if let Some(data_encryption) = &self.headers.data_encryption {
231                headers.insert(
232                    "x-oss-server-side-data-encryption",
233                    data_encryption.parse().unwrap(),
234                );
235            }
236
237            if let Some(encryption_key_id) = &self.headers.encryption_key_id {
238                insert_custom_header(
239                    &mut headers,
240                    "x-oss-server-side-encryption-key-id",
241                    encryption_key_id,
242                );
243            }
244
245            if let Some(object_acl) = &self.headers.object_acl {
246                insert_custom_header(&mut headers, "x-oss-object-acl", object_acl);
247            }
248
249            if let Some(storage_class) = &self.headers.storage_class {
250                insert_custom_header(&mut headers, "x-oss-storage-class", storage_class);
251            }
252
253            if let Some(tags) = &self.headers.oss_tagging {
254                let kv: HashMap<&str, &str> = tags.to_owned().into_iter().collect();
255                let value = serde_qs::to_string(&kv).unwrap();
256                insert_custom_header(&mut headers, "x-oss-tagging", value);
257            }
258
259            if let Some(oss_meta) = &self.headers.oss_meta {
260                for (key, value) in oss_meta {
261                    insert_custom_header(&mut headers, &format!("x-oss-meta-{}", key), value);
262                }
263            }
264            headers
265        }
266
267        pub async fn execute(&self) -> api::ApiResult<()> {
268            let res = format!("/{}/{}", self.client.bucket(), self.object);
269            let url = self.client.object_url(self.object);
270            let headers = self.headers();
271
272            let resp = self
273                .client
274                .request
275                .task()
276                .with_url(&url)
277                .with_method(http::Method::PUT)
278                .with_headers(headers)
279                .with_body(self.content.to_owned())
280                .with_resource(&res)
281                .execute_timeout(self.timeout.unwrap_or(self.client.timeout()))
282                .await?;
283            Ok(ApiResponseFrom(resp).to_empty().await)
284        }
285    }
286
287    #[derive(Debug, Default, Clone)]
288    struct CopyObjectBuilderHeaders<'a> {
289        copy_source: Option<&'a str>,
290        source_version_id: Option<&'a str>,
291        version_id: Option<&'a str>,
292        forbid_overwrite: Option<bool>,
293        if_match: Option<&'a str>,
294        if_none_match: Option<&'a str>,
295        if_unmodified_since: Option<DateTime<Utc>>,
296        if_modified_since: Option<DateTime<Utc>>,
297        metadata_directive: Option<MetadataDirective>,
298        encryption: Option<ServerSideEncryption>,
299        enc_key_id: Option<&'a str>,
300        object_acl: Option<ObjectACL>,
301        storage_class: Option<StorageClass>,
302        oss_tagging: Option<Vec<(&'a str, &'a str)>>,
303        tagging_directive: Option<TaggingDirective>,
304    }
305
306    #[derive(Debug, Clone)]
307    pub struct CopyObjectBuilder<'a> {
308        client: &'a oss::Client<'a>,
309        object: &'a str,
310        headers: CopyObjectBuilderHeaders<'a>,
311    }
312
313    impl<'a> CopyObjectBuilder<'a> {
314        pub(crate) fn new(client: &'a oss::Client, object: &'a str) -> Self {
315            Self {
316                client,
317                object,
318                headers: CopyObjectBuilderHeaders::default(),
319            }
320        }
321
322        /// 指定CopyObject操作时是否覆盖同名目标Object。当目标Bucket处于已开
323        /// 启或已暂停版本控制状态时,x-oss-forbid-overwrite请求Header设置
324        /// 无效,即允许覆盖同名Object。
325        pub fn with_forbid_overwrite(mut self, value: bool) -> Self {
326            self.headers.forbid_overwrite = Some(value);
327            self
328        }
329
330        /// 指定拷贝的源地址
331        pub fn with_copy_source(mut self, value: &'a str) -> Self {
332            self.headers.copy_source = Some(value);
333            self
334        }
335
336        pub fn with_source_version_id(mut self, value: &'a str) -> Self {
337            self.headers.source_version_id = Some(value);
338            self
339        }
340
341        pub fn with_version_id(mut self, value: &'a str) -> Self {
342            self.headers.version_id = Some(value);
343            self
344        }
345
346        /// 如果源Object的ETag值和您提供的ETag相等,则执行拷贝操作,并返回200 OK
347        pub fn with_if_match(mut self, value: &'a str) -> Self {
348            self.headers.if_match = Some(value);
349            self
350        }
351
352        pub fn with_if_none_match(mut self, value: &'a str) -> Self {
353            self.headers.if_none_match = Some(value);
354            self
355        }
356
357        pub fn with_if_unmodified_since(mut self, value: DateTime<Utc>) -> Self {
358            self.headers.if_unmodified_since = Some(value);
359            self
360        }
361
362        pub fn with_if_modified_since(mut self, value: DateTime<Utc>) -> Self {
363            self.headers.if_modified_since = Some(value);
364            self
365        }
366
367        pub fn with_metadata_directive(mut self, value: MetadataDirective) -> Self {
368            self.headers.metadata_directive = Some(value);
369            self
370        }
371
372        pub fn with_encryption(mut self, value: ServerSideEncryption) -> Self {
373            self.headers.encryption = Some(value);
374            self
375        }
376
377        pub fn with_enc_key_id(mut self, value: &'a str) -> Self {
378            self.headers.enc_key_id = Some(value);
379            self
380        }
381
382        pub fn with_object_acl(mut self, value: ObjectACL) -> Self {
383            self.headers.object_acl = Some(value);
384            self
385        }
386
387        pub fn with_storage_class(mut self, value: StorageClass) -> Self {
388            self.headers.storage_class = Some(value);
389            self
390        }
391
392        pub fn with_oss_tagging(mut self, value: Vec<(&'a str, &'a str)>) -> Self {
393            self.headers.oss_tagging = Some(value);
394            self
395        }
396
397        pub fn with_tagging_directive(mut self, value: TaggingDirective) -> Self {
398            self.headers.tagging_directive = Some(value);
399            self
400        }
401
402        fn headers(&self) -> HeaderMap {
403            let mut headers = HeaderMap::new();
404            if let Some(true) = self.headers.forbid_overwrite {
405                insert_custom_header(&mut headers, "x-oss-forbid-overwrite", "true");
406            }
407
408            if let Some(copy_source) = self.headers.copy_source {
409                let value = if let Some(source_version_id) = self.headers.source_version_id {
410                    format!("{}?versionId={}", copy_source, source_version_id)
411                } else {
412                    copy_source.to_string()
413                };
414                let key = "x-oss-copy-source";
415                insert_custom_header(&mut headers, key, value);
416            }
417
418            if let Some(value) = self.headers.if_match {
419                let key = "x-oss-copy-source-if-match";
420                insert_custom_header(&mut headers, key, value);
421            }
422
423            if let Some(value) = self.headers.if_none_match {
424                let key = "x-oss-copy-source-if-none-match";
425                insert_custom_header(&mut headers, key, value);
426            }
427
428            if let Some(value) = &self.headers.if_unmodified_since {
429                let key = "x-oss-copy-source-if-unmodified-since";
430                insert_custom_header(
431                    &mut headers,
432                    key,
433                    value.format(oss::GMT_DATE_FMT).to_string(),
434                )
435            }
436
437            if let Some(value) = &self.headers.if_modified_since {
438                let key = "x-oss-copy-source-if-modified-since";
439                insert_custom_header(
440                    &mut headers,
441                    key,
442                    value.format(oss::GMT_DATE_FMT).to_string(),
443                )
444            }
445
446            if let Some(value) = &self.headers.metadata_directive {
447                let key = "x-oss-metadata-directive";
448                insert_custom_header(&mut headers, key, value.to_string())
449            }
450
451            if let Some(value) = &self.headers.encryption {
452                let key = "x-oss-server-side-encryption";
453                insert_custom_header(&mut headers, key, value.to_string())
454            }
455
456            if let Some(value) = self.headers.enc_key_id {
457                let key = "x-oss-server-side-encryption-key-id";
458                insert_custom_header(&mut headers, key, value)
459            }
460
461            if let Some(value) = &self.headers.object_acl {
462                let key = "x-oss-object-acl";
463                insert_custom_header(&mut headers, key, value.to_string())
464            }
465
466            if let Some(value) = &self.headers.storage_class {
467                let key = "x-oss-storage-class";
468                insert_custom_header(&mut headers, key, value.to_string())
469            }
470
471            if let Some(tags) = &self.headers.oss_tagging {
472                let kv: HashMap<&str, &str> = tags.to_owned().into_iter().collect();
473                let value = serde_qs::to_string(&kv).unwrap();
474                insert_custom_header(&mut headers, "x-oss-tagging", value);
475            }
476
477            if let Some(value) = &self.headers.tagging_directive {
478                let key = "x-oss-tagging-directive";
479                insert_custom_header(&mut headers, key, value.to_string())
480            }
481
482            headers
483        }
484
485        pub async fn execute(&self) -> api::ApiResult<CopyObjectResult> {
486            let res = format!("/{}/{}", self.client.bucket(), self.object);
487            let url = self.client.object_url(self.object);
488            let headers = self.headers();
489            let resp = self
490                .client
491                .request
492                .task()
493                .with_url(&url)
494                .with_method(http::Method::PUT)
495                .with_headers(headers)
496                .with_resource(&res)
497                .execute_timeout(self.client.timeout())
498                .await?;
499            Ok(ApiResponseFrom(resp).to_type().await)
500        }
501    }
502
503    #[derive(Debug, Default)]
504    struct AppendObjectBuilderHeaders<'a> {
505        cache_control: Option<http::CacheControl>,
506        content_disposition: Option<http::ContentDisposition>,
507        content_encoding: Option<http::ContentEncoding>,
508        content_md5: Option<&'a str>,
509        expires: Option<DateTime<Utc>>,
510        encryption: Option<ServerSideEncryption>,
511        object_acl: Option<ObjectACL>,
512        storage_class: Option<StorageClass>,
513        oss_meta: Option<Vec<(&'a str, &'a str)>>,
514        oss_tagging: Option<Vec<(&'a str, &'a str)>>,
515    }
516
517    pub struct AppendObjectBuilder<'a> {
518        client: &'a oss::Client<'a>,
519        object: String,
520        position: usize,
521        content: oss::Bytes,
522        headers: AppendObjectBuilderHeaders<'a>,
523    }
524
525    impl<'a> AppendObjectBuilder<'a> {
526        pub(crate) fn new(client: &'a oss::Client, object: &'a str) -> Self {
527            Self {
528                client,
529                object: object.to_string(),
530                position: 0,
531                content: oss::Bytes::new(),
532                headers: AppendObjectBuilderHeaders::default(),
533            }
534        }
535
536        pub fn with_position(mut self, value: usize) -> Self {
537            self.position = value;
538            self
539        }
540
541        pub fn with_content(mut self, value: oss::Bytes) -> Self {
542            self.content = value;
543            self
544        }
545
546        pub fn with_cache_control(mut self, value: CacheControl) -> Self {
547            self.headers.cache_control = Some(value);
548            self
549        }
550        pub fn with_content_disposition(mut self, value: ContentDisposition) -> Self {
551            self.headers.content_disposition = Some(value);
552            self
553        }
554        pub fn with_content_encoding(mut self, value: ContentEncoding) -> Self {
555            self.headers.content_encoding = Some(value);
556            self
557        }
558
559        pub fn with_expires(mut self, value: DateTime<Utc>) -> Self {
560            self.headers.expires = Some(value);
561            self
562        }
563
564        pub fn with_encryption(mut self, value: ServerSideEncryption) -> Self {
565            self.headers.encryption = Some(value);
566            self
567        }
568
569        pub fn with_object_acl(mut self, value: ObjectACL) -> Self {
570            self.headers.object_acl = Some(value);
571            self
572        }
573
574        pub fn with_storage_class(mut self, value: StorageClass) -> Self {
575            self.headers.storage_class = Some(value);
576            self
577        }
578
579        pub fn with_content_md5(mut self, value: &'a str) -> Self {
580            self.headers.content_md5 = Some(value);
581            self
582        }
583
584        pub fn with_oss_tagging(mut self, value: Vec<(&'a str, &'a str)>) -> Self {
585            self.headers.oss_tagging = Some(value);
586            self
587        }
588
589        pub fn with_oss_meta(mut self, value: Vec<(&'a str, &'a str)>) -> Self {
590            self.headers.oss_meta = Some(value);
591            self
592        }
593
594        #[allow(unused)]
595        fn headers(&self) -> http::HeaderMap {
596            let mut headers = http::HeaderMap::new();
597
598            if let Some(cache_control) = &self.headers.cache_control {
599                insert_header(&mut headers, CACHE_CONTROL, cache_control);
600            }
601
602            if let Some(content_disposition) = &self.headers.content_disposition {
603                insert_header(&mut headers, CONTENT_DISPOSITION, content_disposition);
604            }
605
606            if let Some(content_encoding) = &self.headers.content_encoding {
607                insert_header(&mut headers, CONTENT_ENCODING, content_encoding);
608            }
609
610            if let Some(content_md5) = &self.headers.content_md5 {
611                headers.insert("Content-MD5", content_md5.parse().unwrap());
612            }
613
614            if let Some(expires) = &self.headers.expires {
615                insert_header(&mut headers, EXPIRES, expires.format(oss::GMT_DATE_FMT));
616            }
617
618            if let Some(encryption) = &self.headers.encryption {
619                insert_custom_header(&mut headers, "x-oss-server-side-encryption", encryption);
620            }
621
622            if let Some(object_acl) = &self.headers.object_acl {
623                insert_custom_header(&mut headers, "x-oss-object-acl", object_acl);
624            }
625
626            if let Some(storage_class) = &self.headers.storage_class {
627                insert_custom_header(&mut headers, "x-oss-storage-class", storage_class);
628            }
629
630            if let Some(tags) = &self.headers.oss_tagging {
631                let kv: HashMap<&str, &str> = tags.to_owned().into_iter().collect();
632                let value = serde_qs::to_string(&kv).unwrap();
633                insert_custom_header(&mut headers, "x-oss-tagging", value);
634            }
635
636            if let Some(oss_meta) = &self.headers.oss_meta {
637                for (key, value) in oss_meta {
638                    insert_custom_header(&mut headers, &format!("x-oss-meta-{}", key), value);
639                }
640            }
641            headers
642        }
643
644        pub async fn execute(&self) -> api::ApiResult {
645            let res = format!(
646                "/{}/{}?append&position={}",
647                self.client.bucket(),
648                &self.object,
649                self.position
650            );
651            let url = format!(
652                "{}?append&position={}",
653                self.client.object_url(&self.object),
654                self.position
655            );
656
657            let mut headers = self.headers();
658            headers.insert(CONTENT_LENGTH, self.content.len().into());
659            // dbg!(&url);
660            // dbg!(&headers);
661            let resp = self
662                .client
663                .request
664                .task()
665                .with_url(&url)
666                .with_headers(headers)
667                .with_resource(&res)
668                .with_body(self.content.to_owned())
669                .with_method(http::Method::POST)
670                .execute()
671                .await?;
672            Ok(ApiResponseFrom(resp).to_empty().await)
673        }
674    }
675
676    #[derive(Debug, Default, Serialize, Deserialize)]
677    pub(crate) struct GetObjectBuilderQuery<'a> {
678        #[serde(
679            rename = "response-cache-control",
680            skip_serializing_if = "Option::is_none"
681        )]
682        cache_control: Option<&'a str>,
683        #[serde(
684            rename = "response-content-disposition",
685            skip_serializing_if = "Option::is_none"
686        )]
687        content_disposition: Option<&'a str>,
688        #[serde(
689            rename = "response-content-encoding",
690            skip_serializing_if = "Option::is_none"
691        )]
692        content_encoding: Option<&'a str>,
693        #[serde(
694            rename = "response-content-language",
695            skip_serializing_if = "Option::is_none"
696        )]
697        content_language: Option<&'a str>,
698        #[serde(
699            rename = "response-content-type",
700            skip_serializing_if = "Option::is_none"
701        )]
702        content_type: Option<&'a str>,
703        #[serde(rename = "response-expires", skip_serializing_if = "Option::is_none")]
704        expires: Option<&'a str>,
705        #[serde(rename = "versionId", skip_serializing_if = "Option::is_none")]
706        version_id: Option<&'a str>,
707    }
708
709    #[derive(Debug)]
710    pub struct GetObjectBuilder<'a> {
711        client: &'a oss::Client<'a>,
712        object: &'a str,
713        range: Option<ByteRange>,
714        modified_since: Option<DateTime<Utc>>,
715        unmodified_since: Option<DateTime<Utc>>,
716        r#match: Option<&'a str>,
717        none_match: Option<&'a str>,
718        accept_encoding: Option<&'a str>,
719        query: GetObjectBuilderQuery<'a>,
720        timeout: Option<u64>,
721    }
722
723    impl<'a> GetObjectBuilder<'a> {
724        pub(crate) fn new(client: &'a oss::Client, object: &'a str) -> Self {
725            Self {
726                client,
727                object,
728                range: None,
729                r#match: None,
730                modified_since: None,
731                unmodified_since: None,
732                none_match: None,
733                accept_encoding: None,
734                query: GetObjectBuilderQuery::default(),
735                timeout: None,
736            }
737        }
738
739        pub fn with_version_id(mut self, value: &'a str) -> Self {
740            self.query.version_id = Some(value);
741            self
742        }
743
744        pub fn with_content_type(mut self, value: &'a str) -> Self {
745            self.query.content_type = Some(value);
746            self
747        }
748
749        pub fn with_content_language(mut self, value: &'a str) -> Self {
750            self.query.content_language = Some(value);
751            self
752        }
753
754        pub fn with_expires(mut self, value: &'a str) -> Self {
755            self.query.expires = Some(value);
756            self
757        }
758
759        pub fn with_cache_control(mut self, value: &'a str) -> Self {
760            self.query.cache_control = Some(value);
761            self
762        }
763
764        pub fn with_content_disposition(mut self, value: &'a str) -> Self {
765            self.query.content_disposition = Some(value);
766            self
767        }
768
769        pub fn with_content_encoding(mut self, value: &'a str) -> Self {
770            self.query.content_encoding = Some(value);
771            self
772        }
773
774        pub fn with_range(mut self, value: ByteRange) -> Self {
775            self.range = Some(value);
776            self
777        }
778
779        pub fn with_modified_since(mut self, value: DateTime<Utc>) -> Self {
780            self.modified_since = Some(value);
781            self
782        }
783
784        pub fn with_unmodified_since(mut self, value: DateTime<Utc>) -> Self {
785            self.unmodified_since = Some(value);
786            self
787        }
788
789        pub fn with_match(mut self, value: &'a str) -> Self {
790            self.r#match = Some(value);
791            self
792        }
793
794        pub fn with_none_match(mut self, value: &'a str) -> Self {
795            self.none_match = Some(value);
796            self
797        }
798
799        pub fn with_accept_encoding(mut self, value: &'a str) -> Self {
800            self.accept_encoding = Some(value);
801            self
802        }
803
804        pub fn with_timeout(mut self, value: u64) -> Self {
805            self.timeout = Some(value);
806            self
807        }
808
809        pub(crate) fn query(&self) -> String {
810            serde_qs::to_string(&self.query).unwrap()
811        }
812
813        pub(crate) fn headers(&self) -> http::HeaderMap {
814            let mut headers = http::HeaderMap::new();
815            if let Some(range) = &self.range {
816                insert_header(&mut headers, RANGE, range.to_string());
817            }
818            if let Some(modified_since) = &self.modified_since {
819                insert_header(&mut headers, IF_MODIFIED_SINCE, modified_since);
820            }
821            if let Some(unmodified_since) = &self.unmodified_since {
822                let dt = unmodified_since.format(oss::GMT_DATE_FMT).to_string();
823                insert_header(&mut headers, IF_UNMODIFIED_SINCE, dt);
824            }
825            if let Some(r#match) = &self.r#match {
826                insert_header(&mut headers, IF_MATCH, r#match);
827            }
828            if let Some(none_match) = &self.none_match {
829                insert_header(&mut headers, IF_NONE_MATCH, none_match);
830            }
831            if let Some(accept_encoding) = &self.accept_encoding {
832                insert_header(&mut headers, ACCEPT_ENCODING, accept_encoding);
833            }
834            headers
835        }
836
837        pub async fn execute(&self) -> api::ApiResult<Bytes> {
838            let mut res = format!("/{}/{}", self.client.bucket(), self.object);
839            let mut url = self.client.object_url(self.object);
840            let query = self.query();
841            // dbg!(&query);
842            if !query.is_empty() {
843                res = format!("{}?{}", res, query);
844                url = format!("{}?{}", url, query)
845            }
846
847            let headers = self.headers();
848            let resp = self
849                .client
850                .request
851                .task()
852                .with_url(&url)
853                .with_headers(headers)
854                .with_resource(&res)
855                .execute_timeout(self.timeout.unwrap_or(self.client.timeout()))
856                .await?;
857
858            Ok(ApiResponseFrom(resp).to_bytes().await)
859        }
860    }
861
862    #[derive(Debug)]
863    pub struct DeleteObjectBuilder<'a> {
864        client: &'a oss::Client<'a>,
865        object: &'a str,
866        version_id: Option<&'a str>,
867    }
868
869    impl<'a> DeleteObjectBuilder<'a> {
870        pub fn new(client: &'a oss::Client, object: &'a str) -> Self {
871            Self {
872                client,
873                object,
874                version_id: None,
875            }
876        }
877
878        pub fn with_version_id(mut self, value: &'a str) -> Self {
879            self.version_id = Some(value);
880            self
881        }
882
883        pub async fn execute(&self) -> api::ApiResult<()> {
884            let mut res = format!("/{}/{}", self.client.bucket(), self.object);
885            let mut url = self.client.object_url(self.object);
886            if let Some(version_id) = self.version_id {
887                res = format!("{}?versionId={}", res, version_id);
888                url = format!("{}?versionId={}", url, version_id);
889            }
890
891            let resp = self
892                .client
893                .request
894                .task()
895                .with_url(&url)
896                .with_resource(&res)
897                .with_method(http::Method::DELETE)
898                .execute()
899                .await?;
900
901            Ok(ApiResponseFrom(resp).to_empty().await)
902        }
903    }
904
905    #[allow(unused)]
906    pub struct DeleteMultipleObjectsBuilder<'a> {
907        client: &'a oss::Client<'a>,
908        quiet: Option<bool>,
909        encoding_type: Option<&'a str>,
910        deletes: Vec<(&'a str, &'a str)>,
911        content_length: Option<u64>,
912        content_md5: Option<&'a str>,
913    }
914
915    impl<'a> DeleteMultipleObjectsBuilder<'a> {
916        pub fn new(client: &'a oss::Client) -> Self {
917            Self {
918                client,
919                quiet: Some(false),
920                deletes: Vec::new(),
921                encoding_type: None,
922                content_length: None,
923                content_md5: None,
924            }
925        }
926
927        /// 添加删除目标 Vec<(object, version_id)>
928        ///
929        pub fn with_deletes(mut self, value: Vec<(&'a str, &'a str)>) -> Self {
930            self.deletes = value;
931            self
932        }
933
934        pub fn with_quiet(mut self, value: bool) -> Self {
935            self.quiet = Some(value);
936            self
937        }
938
939        pub fn with_encoding_type(mut self, value: &'a str) -> Self {
940            self.encoding_type = Some(value);
941            self
942        }
943
944        fn content(&self) -> Delete {
945            Delete {
946                quiet: self.quiet,
947                object: self
948                    .deletes
949                    .iter()
950                    .map(|item| Object {
951                        key: item.0.to_string(),
952                        version_id: if item.1.is_empty() {
953                            None
954                        } else {
955                            Some(item.1.to_string())
956                        },
957                    })
958                    .collect(),
959            }
960        }
961
962        pub async fn execute(&self) -> api::ApiResult<DeleteResult> {
963            let resp = self.inner_execute(false).await?;
964            Ok(ApiResponseFrom(resp).to_type().await)
965        }
966
967        pub async fn execute_quiet(&self) -> api::ApiResult {
968            let resp = self.inner_execute(true).await?;
969            Ok(ApiResponseFrom(resp).to_empty().await)
970        }
971
972        async fn inner_execute(&self, quiet: bool) -> oss::Result<Response> {
973            let res = format!("/{}/?{}", self.client.bucket(), "delete");
974            let url = format!("{}?{}", self.client.base_url(), "delete");
975            let mut content = self.content();
976            content.quiet = Some(quiet);
977            let content = quick_xml::se::to_string(&content).unwrap();
978            let content = oss::Bytes::from(content);
979
980            let mut headers = http::header::HeaderMap::new();
981            headers.insert(CONTENT_LENGTH, content.len().into());
982            let content_md5 = oss_md5(&content).unwrap();
983            headers.insert("Content-MD5", content_md5.parse().unwrap());
984            if let Some(encoding_type) = self.encoding_type {
985                headers.insert("Encoding-type", encoding_type.parse().unwrap());
986            }
987
988            self.client
989                .request
990                .task()
991                .with_url(&url)
992                .with_method(http::Method::POST)
993                .with_headers(headers)
994                .with_resource(&res)
995                .with_body(content)
996                .execute_timeout(self.client.timeout())
997                .await
998        }
999    }
1000
1001    pub struct HeadObjectBuilder<'a> {
1002        client: &'a oss::Client<'a>,
1003        object: &'a str,
1004        version_id: Option<&'a str>,
1005        modified_since: Option<DateTime<Utc>>,
1006        unmodified_since: Option<DateTime<Utc>>,
1007        r#match: Option<&'a str>,
1008        none_match: Option<&'a str>,
1009    }
1010
1011    impl<'a> HeadObjectBuilder<'a> {
1012        pub(crate) fn new(client: &'a oss::Client, object: &'a str) -> Self {
1013            Self {
1014                client,
1015                object,
1016                version_id: None,
1017                modified_since: None,
1018                unmodified_since: None,
1019                r#match: None,
1020                none_match: None,
1021            }
1022        }
1023
1024        pub fn with_version_id(mut self, version_id: &'a str) -> Self {
1025            self.version_id = Some(version_id);
1026            self
1027        }
1028
1029        pub fn with_modified_since(mut self, value: DateTime<Utc>) -> Self {
1030            self.modified_since = Some(value);
1031            self
1032        }
1033
1034        pub fn with_unmodified_since(mut self, value: DateTime<Utc>) -> Self {
1035            self.unmodified_since = Some(value);
1036            self
1037        }
1038
1039        pub fn with_match(mut self, value: &'a str) -> Self {
1040            self.r#match = Some(value);
1041            self
1042        }
1043
1044        pub fn with_none_match(mut self, value: &'a str) -> Self {
1045            self.none_match = Some(value);
1046            self
1047        }
1048
1049        fn headers(&self) -> http::HeaderMap {
1050            let mut headers = http::HeaderMap::new();
1051            if let Some(modified_since) = self.modified_since {
1052                insert_header(
1053                    &mut headers,
1054                    IF_MODIFIED_SINCE,
1055                    modified_since.format(oss::GMT_DATE_FMT).to_string(),
1056                );
1057            }
1058
1059            if let Some(unmodified_since) = self.unmodified_since {
1060                insert_header(
1061                    &mut headers,
1062                    IF_UNMODIFIED_SINCE,
1063                    unmodified_since.format(oss::GMT_DATE_FMT).to_string(),
1064                );
1065            }
1066            if let Some(r#match) = self.r#match {
1067                insert_header(&mut headers, IF_MATCH, r#match);
1068            }
1069            if let Some(none_match) = self.none_match {
1070                insert_header(&mut headers, IF_NONE_MATCH, none_match);
1071            }
1072            headers
1073        }
1074
1075        pub async fn execute(&self) -> api::ApiResult {
1076            let mut res = format!("/{}/{}", self.client.bucket(), self.object);
1077            let mut url = self.client.object_url(self.object);
1078            if let Some(version_id) = self.version_id {
1079                res = format!("{}?versionId={}", res, version_id);
1080                url = format!("{}?versionId={}", url, version_id);
1081            };
1082
1083            let resp = self
1084                .client
1085                .request
1086                .task()
1087                .with_url(&url)
1088                .with_method(http::Method::HEAD)
1089                .with_headers(self.headers())
1090                .with_resource(&res)
1091                .execute_timeout(self.client.timeout())
1092                .await?;
1093
1094            Ok(ApiResponseFrom(resp).to_empty().await)
1095        }
1096    }
1097
1098    pub struct GetObjectMetaBuilder<'a> {
1099        client: &'a oss::Client<'a>,
1100        object: &'a str,
1101        version_id: Option<&'a str>,
1102    }
1103
1104    impl<'a> GetObjectMetaBuilder<'a> {
1105        pub(crate) fn new(client: &'a oss::Client, object: &'a str) -> Self {
1106            Self {
1107                client,
1108                object,
1109                version_id: None,
1110            }
1111        }
1112
1113        pub fn with_version_id(mut self, version_id: &'a str) -> Self {
1114            self.version_id = Some(version_id);
1115            self
1116        }
1117
1118        pub async fn execute(&self) -> api::ApiResult<()> {
1119            let mut res = format!(
1120                "/{}/{}?{}",
1121                self.client.options.bucket, self.object, "objectMeta"
1122            );
1123
1124            let mut url = format!("{}?{}", self.client.object_url(self.object), "objectMeta");
1125            // dbg!(&url);
1126
1127            if let Some(version_id) = self.version_id {
1128                res = format!("{}&versionId={}", res, version_id);
1129                url = format!("{}&versionId={}", url, version_id);
1130            }
1131
1132            let resp = self
1133                .client
1134                .request
1135                .task()
1136                .with_url(&url)
1137                .with_resource(&res)
1138                .execute()
1139                .await?;
1140
1141            Ok(ApiResponseFrom(resp).to_empty().await)
1142        }
1143    }
1144
1145    pub struct RestoreObjectBuilder<'a> {
1146        client: &'a oss::Client<'a>,
1147        object: &'a str,
1148        version_id: Option<&'a str>,
1149        days: Option<u8>,
1150        tier: Option<Tier>,
1151    }
1152
1153    impl<'a> RestoreObjectBuilder<'a> {
1154        pub fn new(client: &'a oss::Client, object: &'a str) -> Self {
1155            Self {
1156                client,
1157                object,
1158                version_id: None,
1159                days: None,
1160                tier: None,
1161            }
1162        }
1163
1164        pub fn with_days(mut self, days: u8) -> Self {
1165            self.days = Some(days);
1166            self
1167        }
1168
1169        pub fn with_tier(mut self, tier: Tier) -> Self {
1170            self.tier = Some(tier);
1171            self
1172        }
1173
1174        fn config(&self) -> Option<String> {
1175            let days = self.days?;
1176            let request = RestoreRequest {
1177                days,
1178                job_parameters: self
1179                    .tier
1180                    .as_ref()
1181                    .map(|tier| JobParameters { tier: tier.clone() }),
1182            };
1183            quick_xml::se::to_string(&request).ok()
1184        }
1185
1186        pub async fn execute(&self) -> api::ApiResult<()> {
1187            let mut res = format!("/{}/{}?{}", self.client.bucket(), self.object, "restore");
1188            let mut url = format!("{}?{}", self.client.object_url(self.object), "restore");
1189            if let Some(version_id) = self.version_id {
1190                res = format!("{}&versionId={}", res, version_id);
1191                url = format!("{}&versionId={}", url, version_id);
1192            };
1193
1194            let config = Bytes::from(self.config().unwrap_or("".to_string()));
1195
1196            let resp = self
1197                .client
1198                .request
1199                .task()
1200                .with_url(&url)
1201                .with_method(http::Method::POST)
1202                .with_body(config)
1203                .with_resource(&res)
1204                .execute()
1205                .await?;
1206
1207            Ok(ApiResponseFrom(resp).to_empty().await)
1208        }
1209    }
1210}
1211
1212/// # 基础操作
1213#[allow(non_snake_case)]
1214impl<'a> oss::Client<'a> {
1215    /// 调用PutObject接口上传文件`Object`
1216    ///
1217    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/putobject)
1218    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_put.rs)
1219    pub fn PutObject(&self, object: &'a str) -> PutObjectBuilder {
1220        PutObjectBuilder::new(self, object)
1221    }
1222
1223    /// GetObject接口用于获取某个文件`Object`。此操作需要对此Object具有读权限
1224    ///
1225    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/getobject)
1226    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_get.rs)
1227    pub fn GetObject(&self, object: &'a str) -> GetObjectBuilder {
1228        GetObjectBuilder::new(self, object)
1229    }
1230
1231    /// 调用CopyObject接口拷贝同一地域下相同或不同存储空间`Bucket`之间的文件`Object`
1232    ///
1233    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/copyobject)
1234    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_copy.rs)
1235    pub fn CopyObject(&self, object: &'a str) -> CopyObjectBuilder {
1236        CopyObjectBuilder::new(self, object)
1237    }
1238
1239    /// 调用AppendObject接口用于以追加写的方式上传文件`Object`。通过AppendObject操
1240    ///
1241    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/appendobject)
1242    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_append.rs)
1243    pub fn AppendObject(&self, object: &'a str) -> AppendObjectBuilder {
1244        AppendObjectBuilder::new(self, object)
1245    }
1246
1247    /// 调用DeleteObject删除某个文件`Object`
1248    ///
1249    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/deleteobject)
1250    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_del.rs)
1251    pub fn DeleteObject(&self, object: &'a str) -> DeleteObjectBuilder {
1252        DeleteObjectBuilder::new(self, object)
1253    }
1254
1255    /// DeleteMultipleObjects接口用于删除同一个存储空间`Bucket`中的多个文件`Object`
1256    ///
1257    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/deletemultipleobjects)
1258    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_mutil_del.rs)
1259    pub fn DeleteMultipleObjects(&self) -> DeleteMultipleObjectsBuilder {
1260        DeleteMultipleObjectsBuilder::new(self)
1261    }
1262
1263    /// HeadObject接口用于获取某个文件`Object`的元信息
1264    ///
1265    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/headobject)
1266    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_head.rs)
1267    pub fn HeadObject(&self, object: &'a str) -> HeadObjectBuilder {
1268        HeadObjectBuilder::new(self, object)
1269    }
1270
1271    /// 调用GetObjectMeta接口获取一个文件`Object`的元数据信息
1272    /// 包括该Object的ETag、Size、LastModified信息,并且不返回该Object的内容。
1273    ///
1274    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/getobjectmeta)
1275    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_meta.rs)
1276    pub fn GetObjectMeta(&self, object: &'a str) -> GetObjectMetaBuilder {
1277        GetObjectMetaBuilder::new(self, object)
1278    }
1279
1280    /// 调用RestoreObject接口解冻归档类型、冷归档、深度冷归档类型的文件`Object`
1281    ///
1282    /// - [official docs](https://help.aliyun.com/zh/oss/developer-reference/restoreobject)
1283    /// - [xtoss example](https://github.com/isme-sun/xt_oss/blob/main/examples/api_object_stand_restore.rs)
1284    pub fn RestoreObject(&self, object: &'a str) -> RestoreObjectBuilder {
1285        RestoreObjectBuilder::new(self, object)
1286    }
1287}
1288
1289#[cfg(test)]
1290mod tests {
1291    use super::*;
1292    use crate::oss::{self, http::ContentDisposition};
1293    use crate::util::ByteRange;
1294    use chrono::Utc;
1295    #[test]
1296    fn get_object_builder_arugments() {
1297        let option = oss::Options::default();
1298        let client = oss::Client::new(option);
1299        let filename = Some("测试.txt".to_string());
1300        let content_disposition = ContentDisposition::ATTACHMENT(filename).to_string();
1301
1302        let builder = GetObjectBuilder::new(&client, "example/ex1.txt")
1303            .with_version_id("version123")
1304            .with_content_type("text/plain")
1305            .with_content_language("zh-CN")
1306            .with_expires("expires")
1307            .with_cache_control("cache")
1308            .with_content_disposition(content_disposition.as_str())
1309            .with_content_encoding("GZIP")
1310            .with_range(ByteRange::new().with_start(500).with_amount(1000))
1311            .with_modified_since(Utc::now())
1312            .with_unmodified_since(Utc::now())
1313            .with_match("etag")
1314            .with_none_match("etag")
1315            .with_accept_encoding("text/plain");
1316
1317        let left = r#"response-cache-control=cache&response-content-disposition=attachment%3Bfilename%3D%22%E6%B5%8B%E8%AF%95.txt%22&response-content-encoding=GZIP&response-content-language=zh-CN&response-content-type=text%2Fplain&response-expires=expires&versionId=version123"#;
1318
1319        assert_eq!(left, builder.query());
1320    }
1321}