Skip to main content

ufile_rus3/api/
head_file.rs

1//! This module contains an api to get the metadata of a file from the remote server ucloud.cn.
2
3use std::collections::HashMap;
4
5use anyhow::Error;
6use chrono::Local;
7use reqwest::{Method, header::HeaderMap};
8
9use crate::{
10    AuthorizationService,
11    api::{
12        ApiOperation,
13        object::{BaseResponse, HeadFileResponse, ObjectOptAuthParamBuilder},
14    },
15    define_api_request, define_operation_struct,
16};
17define_operation_struct!(HeadFileOperation);
18
19define_api_request!(HeadFileRequest,
20HeadFileOperationBuilder,
21HeadFileResponse,
22{
23    /// Required: Bucket name
24    #[builder(setter(into))]
25    pub bucket_name: String,
26
27    /// Required: Key name of the file or object name.
28    #[builder(setter(into))]
29    pub key_name: String,
30
31    /// Optional: `STS` temporary security token. but not implementated at now.
32    #[builder(setter(into, strip_option), default)]
33    pub security_token: Option<String>,
34});
35
36#[async_trait::async_trait]
37impl ApiOperation for HeadFileOperation {
38    type Request = HeadFileRequest;
39    type Response = HeadFileResponse;
40    type Error = Error;
41
42    async fn execute(&self, req: Self::Request) -> Result<Self::Response, Self::Error> {
43        let HeadFileRequest {
44            bucket_name,
45            key_name,
46            security_token,
47            ..
48        } = req;
49        let date = Local::now().format("&Y%m%d%H%M%S").to_string();
50        let auth_object = ObjectOptAuthParamBuilder::default()
51            .method(Method::HEAD)
52            .bucket(bucket_name.clone())
53            .key_name(key_name.clone())
54            .content_type("application/json")
55            .date(date.as_str())
56            .build()?;
57        let authorization =
58            AuthorizationService.authorization(auth_object, self.object_config.clone())?;
59        let mut headers = HeaderMap::new();
60        headers.insert("Content-Type", "application/json".parse().unwrap());
61        headers.insert("Accept", "*/*".parse().unwrap());
62        headers.insert("Date", date.parse().unwrap());
63        headers.insert("Authorization", authorization.parse().unwrap());
64        if let Some(ref security_token) = security_token
65            && !security_token.is_empty()
66        {
67            headers.insert("SecurityToken", security_token.parse().unwrap());
68        }
69        let url = self
70            .object_config
71            .generate_final_host(bucket_name.as_str(), key_name.as_str());
72        // Request to get the file metadata containing content-size and content-type.
73        let resp = self
74            .client
75            .get_client()
76            .head(url)
77            .headers(headers)
78            .send()
79            .await?;
80        ::tracing::debug!("get file head response: {:?}", resp);
81        if resp.status().is_success() {
82            // Request success.
83            let headers = resp
84                .headers()
85                .into_iter()
86                .map(|(k, v)| {
87                    (
88                        k.as_str().to_lowercase(),
89                        v.to_str().unwrap().to_lowercase(),
90                    )
91                })
92                .collect::<HashMap<String, String>>();
93            return Ok(HeadFileResponse {
94                content_type: headers.get("content-type").unwrap().into(),
95                content_length: headers
96                    .get("content-length")
97                    .unwrap()
98                    .parse::<u64>()
99                    .unwrap(),
100                etag: headers.get("etag").map(|v| v.to_string()),
101                last_modified: headers.get("last-modified").map(Into::into),
102                headers: Some(headers),
103            });
104        }
105        let resp = resp.json::<BaseResponse>().await?;
106        tracing::debug!(
107            "Failed to get file head for: {} with error: {:?}",
108            key_name,
109            resp
110        );
111        Err(Error::msg("Failed to get file head"))
112    }
113}