aliyun_oss_rs/object/
multipart_init_upload.rs

1use crate::common::body_to_bytes;
2use crate::{
3    common::{
4        Acl, CacheControl, ContentDisposition, StorageClass, invalid_metadata_key, url_encode,
5    },
6    error::{Error, normal_error},
7    request::{Oss, OssRequest},
8};
9use http::{Method, header};
10use serde_derive::Deserialize;
11use std::collections::HashMap;
12
13// Returned content
14#[derive(Debug, Deserialize)]
15#[serde(rename_all = "PascalCase")]
16struct InitiateMultipartUploadResult {
17    upload_id: String,
18}
19
20/// Initiate a multipart upload
21///
22/// See the [Alibaba Cloud documentation](https://help.aliyun.com/document_detail/31992.html) for details
23pub struct InitUpload {
24    req: OssRequest,
25    tags: HashMap<String, String>,
26}
27impl InitUpload {
28    pub(super) fn new(oss: Oss) -> Self {
29        let mut req = OssRequest::new(oss, Method::POST);
30        req.insert_query("uploads", "");
31        InitUpload {
32            req,
33            tags: HashMap::new(),
34        }
35    }
36    /// Set the file's MIME type
37    ///
38    /// If no MIME type is set, the default (application/octet-stream) is used
39    pub fn set_mime(mut self, mime: impl ToString) -> Self {
40        self.req.insert_header(header::CONTENT_TYPE, mime);
41        self
42    }
43    /// Set the file's access permissions
44    pub fn set_acl(mut self, acl: Acl) -> Self {
45        self.req.insert_header("x-oss-object-acl", acl);
46        self
47    }
48    /// Set the file's storage class
49    pub fn set_storage_class(mut self, storage_class: StorageClass) -> Self {
50        self.req.insert_header("x-oss-storage-class", storage_class);
51        self
52    }
53    /// Cache behavior of the webpage when the file is downloaded
54    pub fn set_cache_control(mut self, cache_control: CacheControl) -> Self {
55        self.req.insert_header(header::CACHE_CONTROL, cache_control);
56        self
57    }
58    /// Set how the file is presented
59    pub fn set_content_disposition(mut self, content_disposition: ContentDisposition) -> Self {
60        self.req
61            .insert_header(header::CONTENT_DISPOSITION, content_disposition);
62        self
63    }
64    /// Disallow overwriting files with the same name
65    pub fn forbid_overwrite(mut self) -> Self {
66        self.req.insert_header("x-oss-forbid-overwrite", "true");
67        self
68    }
69    /// Set additional metadata
70    ///
71    /// Keys may only contain letters, numbers, and hyphens; metadata with other characters will be discarded
72    pub fn set_meta(mut self, key: impl ToString, value: impl ToString) -> Self {
73        let key = key.to_string();
74        if !invalid_metadata_key(&key) {
75            self.req
76                .insert_header(format!("x-oss-meta-{}", key.to_string()), value);
77        }
78        self
79    }
80    /// Set tag information
81    pub fn set_tagging(mut self, key: impl ToString, value: impl ToString) -> Self {
82        self.tags.insert(key.to_string(), value.to_string());
83        self
84    }
85    /// Send the request
86    pub async fn send(mut self) -> Result<String, Error> {
87        // Insert tags
88        let tags = self
89            .tags
90            .into_iter()
91            .map(|(key, value)| {
92                if value.is_empty() {
93                    url_encode(&key.to_string())
94                } else {
95                    format!(
96                        "{}={}",
97                        url_encode(&key.to_string()),
98                        url_encode(&value.to_string())
99                    )
100                }
101            })
102            .collect::<Vec<_>>()
103            .join("&");
104        if !tags.is_empty() {
105            self.req.insert_header("x-oss-tagging", tags);
106        }
107        // Upload file
108        let response = self.req.send_to_oss()?.await?;
109        // Parse the response
110        let status_code = response.status();
111        match status_code {
112            code if code.is_success() => {
113                let response_bytes = body_to_bytes(response.into_body())
114                    .await
115                    .map_err(|_| Error::OssInvalidResponse(None))?;
116                let result: InitiateMultipartUploadResult =
117                    serde_xml_rs::from_reader(&*response_bytes)
118                        .map_err(|_| Error::OssInvalidResponse(Some(response_bytes)))?;
119                Ok(result.upload_id)
120            }
121            _ => Err(normal_error(response).await),
122        }
123    }
124}