Skip to main content

ufile_rus3/api/
put_file.rs

1use reqwest::header::{HeaderMap, HeaderName};
2use std::str::FromStr;
3
4use crate::api::{object::PutObjectResultResponse, traits::ApiOperation};
5
6use anyhow::Error;
7use chrono::Local;
8use reqwest::Method;
9
10use crate::api::object::ObjectOptAuthParamBuilder;
11
12use crate::{AuthorizationService, define_api_request, define_operation_struct};
13
14define_operation_struct!(PutFileOperation);
15
16define_api_request!(
17    PutFileRequest,
18    PutFileOperationBuilder,
19    PutObjectResultResponse,
20    {
21        /// Required: Bucket name
22        #[builder(setter(into))]
23        pub bucket_name: String,
24
25        /// Required: Object key name
26        #[builder(setter(into))]
27        pub key_name: String,
28
29        /// Required: File MIME type
30        #[builder(setter(into))]
31        pub mime_type: String,
32
33        /// Required: File stream.
34        pub stream: crate::api::ByteStream,
35
36        /// Optional: File length
37        pub content_length: usize,
38
39        /// Optional: File MD5 checksum
40        #[builder(setter(into, strip_option), default)]
41        pub content_md5: ::std::option::Option<String>,
42
43        /// Optional: User custom metadata
44        #[builder(setter(strip_option), default)]
45        pub metadatas: ::std::option::Option<::std::collections::HashMap<String, String>>,
46
47        /// Optional: Storage type: STANDARD | IA | ARCHIVE
48        #[builder(setter(into, strip_option), default)]
49        pub storage_type: ::std::option::Option<String>,
50
51        /// Optional: Image processing service
52        #[builder(setter(into, strip_option), default)]
53        pub iop_cmd: ::std::option::Option<String>,
54
55        /// Optional: Security token
56        #[builder(setter(into, strip_option), default)]
57        pub security_token: ::std::option::Option<String>,
58    }
59);
60
61#[async_trait::async_trait]
62impl ApiOperation for PutFileOperation {
63    type Request = PutFileRequest;
64    type Response = PutObjectResultResponse;
65    type Error = Error;
66
67    async fn execute(&self, req: Self::Request) -> Result<Self::Response, Self::Error> {
68        let PutFileRequest {
69            bucket_name,
70            key_name,
71            stream,
72            mime_type,
73            metadatas,
74            content_length,
75            content_md5,
76            storage_type,
77            iop_cmd,
78            security_token,
79            ..
80        } = req;
81        let date = Local::now().format("%Y%m%d%H%M%S").to_string();
82        let content_type = mime_type.clone();
83        let mut auth_object_builder = ObjectOptAuthParamBuilder::default();
84        auth_object_builder
85            .method(Method::PUT)
86            .bucket(bucket_name.as_str())
87            .key_name(key_name.as_str())
88            .content_type(content_type.as_str())
89            .date(date.as_str());
90
91        let mut headers = HeaderMap::new();
92        // add content md5 to auth object
93        if let Some(content_md5) = content_md5 {
94            auth_object_builder.content_md5(content_md5.as_str());
95            headers.insert("Content-MD5", content_md5.parse().unwrap());
96        }
97        let auth_object = auth_object_builder.build()?;
98        headers.insert(
99            "Content-Length",
100            content_length.to_string().parse().unwrap(),
101        );
102
103        let authorization =
104            AuthorizationService.authorization(auth_object, self.object_config.clone())?;
105        headers.insert("Authorization", authorization.parse().unwrap());
106        headers.insert("Content-Type", content_type.parse().unwrap());
107        headers.insert("Accept", "*/*".parse().unwrap());
108        headers.insert("Date", date.parse().unwrap());
109
110        if let Some(storage_type) = storage_type {
111            headers.insert("X-Ufile-Storage-Class", storage_type.parse().unwrap());
112        }
113
114        if let Some(security_token) = security_token {
115            headers.insert("SecurityToken", security_token.parse().unwrap());
116        }
117
118        if let Some(metadatas) = metadatas
119            && !metadatas.is_empty()
120        {
121            metadatas.iter().for_each(|(key, value)| {
122                let key = format!("X-Ufile-Meta-{key}");
123                headers.insert(
124                    HeaderName::from_str(key.as_str()).unwrap(),
125                    value.to_string().parse().unwrap(),
126                );
127            });
128        }
129
130        let mut url = self
131            .object_config
132            .generate_final_host(bucket_name.as_str(), key_name.as_str());
133        if let Some(iop_cmd) = iop_cmd {
134            url = format!("{url}?{iop_cmd}");
135        }
136
137        let response = self
138            .client
139            .send_file(url.as_str(), Method::PUT, headers, stream)
140            .await?;
141        tracing::debug!("put file response: {:?}", response);
142        let mut put_file_response = PutObjectResultResponse::from(response);
143        if let Some(e_tag) = put_file_response.resp.headers.get("etag") {
144            put_file_response.etag = e_tag.to_string();
145        }
146
147        Ok(put_file_response)
148    }
149}