s3/bucket/
get.rs

1use crate::bucket::{Bucket, Request};
2use crate::command::Command;
3use crate::error::S3Error;
4use crate::request::RequestImpl;
5use crate::request::ResponseData;
6
7use crate::request::{AsyncWrite, ResponseDataStream};
8
9impl Bucket {
10    /// Gets file from an S3 path.
11    ///
12    /// # Example:
13    ///
14    /// ```rust,no_run
15    /// use s3::bucket::Bucket;
16    /// use s3::creds::Credentials;
17    /// use anyhow::Result;
18    ///
19    /// # #[tokio::main]
20    /// # async fn main() -> Result<()> {
21    ///
22    /// let bucket_name = "rust-s3-test";
23    /// let region = "us-east-1".parse()?;
24    /// let credentials = Credentials::default()?;
25    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
26    ///
27    /// let response_data = bucket.get_object("/test.file").await?;
28    /// # Ok(())
29    /// # }
30    /// ```
31    pub async fn get_object<S: AsRef<str>>(&self, path: S) -> Result<ResponseData, S3Error> {
32        let command = Command::GetObject;
33        let request = RequestImpl::new(self, path.as_ref(), command)?;
34        request.response_data(false).await
35    }
36
37    /// Gets torrent from an S3 path.
38    ///
39    /// # Example:
40    ///
41    /// ```rust,no_run
42    /// use s3::bucket::Bucket;
43    /// use s3::creds::Credentials;
44    /// use anyhow::Result;
45    ///
46    /// # #[tokio::main]
47    /// # async fn main() -> Result<()> {
48    ///
49    /// let bucket_name = "rust-s3-test";
50    /// let region = "us-east-1".parse()?;
51    /// let credentials = Credentials::default()?;
52    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
53    ///
54    /// let response_data = bucket.get_object_torrent("/test.file").await?;
55    /// # Ok(())
56    /// # }
57    /// ```
58    pub async fn get_object_torrent<S: AsRef<str>>(
59        &self,
60        path: S,
61    ) -> Result<ResponseData, S3Error> {
62        let command = Command::GetObjectTorrent;
63        let request = RequestImpl::new(self, path.as_ref(), command)?;
64        request.response_data(false).await
65    }
66
67    /// Gets specified inclusive byte range of file from an S3 path.
68    ///
69    /// # Example:
70    ///
71    /// ```rust,no_run
72    /// use s3::bucket::Bucket;
73    /// use s3::creds::Credentials;
74    /// use anyhow::Result;
75    ///
76    /// # #[tokio::main]
77    /// # async fn main() -> Result<()> {
78    ///
79    /// let bucket_name = "rust-s3-test";
80    /// let region = "us-east-1".parse()?;
81    /// let credentials = Credentials::default()?;
82    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
83    ///
84    /// let response_data = bucket.get_object_range("/test.file", 0, Some(31)).await?;
85    /// #
86    /// # Ok(())
87    /// # }
88    /// ```
89    pub async fn get_object_range<S: AsRef<str>>(
90        &self,
91        path: S,
92        start: u64,
93        end: Option<u64>,
94    ) -> Result<ResponseData, S3Error> {
95        if let Some(end) = end {
96            assert!(start < end);
97        }
98
99        let command = Command::GetObjectRange { start, end };
100        let request = RequestImpl::new(self, path.as_ref(), command)?;
101        request.response_data(false).await
102    }
103
104    /// Stream range of bytes from S3 path to a local file, generic over T: Write.
105    ///
106    /// # Example:
107    ///
108    /// ```rust,no_run
109    /// use s3::bucket::Bucket;
110    /// use s3::creds::Credentials;
111    /// use anyhow::Result;
112    /// use std::fs::File;
113    ///
114    /// # #[tokio::main]
115    /// # async fn main() -> Result<()> {
116    ///
117    /// let bucket_name = "rust-s3-test";
118    /// let region = "us-east-1".parse()?;
119    /// let credentials = Credentials::default()?;
120    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
121    /// let mut output_file = File::create("output_file").expect("Unable to create file");
122    /// let mut async_output_file = tokio::fs::File::create("async_output_file").await.expect("Unable to create file");
123    /// #[cfg(feature = "with-async-std")]
124    /// let mut async_output_file = async_std::fs::File::create("async_output_file").await.expect("Unable to create file");
125    ///
126    /// let start = 0;
127    /// let end = Some(1024);
128    ///
129    /// let status_code = bucket.get_object_range_to_writer("/test.file", start, end, &mut async_output_file).await?;
130    /// #
131    /// # Ok(())
132    /// # }
133    /// ```
134    pub async fn get_object_range_to_writer<T: AsyncWrite + Send + Unpin, S: AsRef<str>>(
135        &self,
136        path: S,
137        start: u64,
138        end: Option<u64>,
139        writer: &mut T,
140    ) -> Result<u16, S3Error> {
141        if let Some(end) = end {
142            assert!(start < end);
143        }
144
145        let command = Command::GetObjectRange { start, end };
146        let request = RequestImpl::new(self, path.as_ref(), command)?;
147        request.response_data_to_writer(writer).await
148    }
149
150    /// Stream file from S3 path to a local file, generic over T: Write.
151    ///
152    /// # Example:
153    ///
154    /// ```rust,no_run
155    /// use s3::bucket::Bucket;
156    /// use s3::creds::Credentials;
157    /// use anyhow::Result;
158    /// use std::fs::File;
159    ///
160    /// # #[tokio::main]
161    /// # async fn main() -> Result<()> {
162    ///
163    /// let bucket_name = "rust-s3-test";
164    /// let region = "us-east-1".parse()?;
165    /// let credentials = Credentials::default()?;
166    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
167    /// let mut output_file = File::create("output_file").expect("Unable to create file");
168    /// let mut async_output_file = tokio::fs::File::create("async_output_file").await.expect("Unable to create file");
169    /// #[cfg(feature = "with-async-std")]
170    /// let mut async_output_file = async_std::fs::File::create("async_output_file").await.expect("Unable to create file");
171    ///
172    /// let status_code = bucket.get_object_to_writer("/test.file", &mut async_output_file).await?;
173    /// #
174    /// # Ok(())
175    /// # }
176    /// ```
177    pub async fn get_object_to_writer<T: AsyncWrite + Send + Unpin, S: AsRef<str>>(
178        &self,
179        path: S,
180        writer: &mut T,
181    ) -> Result<u16, S3Error> {
182        let command = Command::GetObject;
183        let request = RequestImpl::new(self, path.as_ref(), command)?;
184        request.response_data_to_writer(writer).await
185    }
186
187    /// Stream file from S3 path to a local file using an async stream.
188    ///
189    /// # Example
190    ///
191    /// ```rust,no_run
192    /// use s3::bucket::Bucket;
193    /// use s3::creds::Credentials;
194    /// use anyhow::Result;
195    /// use tokio_stream::StreamExt;
196    /// use tokio::io::AsyncWriteExt;
197    ///
198    /// # #[tokio::main]
199    /// # async fn main() -> Result<()> {
200    ///
201    /// let bucket_name = "rust-s3-test";
202    /// let region = "us-east-1".parse()?;
203    /// let credentials = Credentials::default()?;
204    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
205    /// let path = "path";
206    ///
207    /// let mut response_data_stream = bucket.get_object_stream(path).await?;
208    ///
209    /// let mut async_output_file = tokio::fs::File::create("async_output_file").await.expect("Unable to create file");
210    ///
211    /// while let Some(chunk) = response_data_stream.bytes().next().await {
212    ///     async_output_file.write_all(&chunk.unwrap()).await?;
213    /// }
214    ///
215    /// #
216    /// # Ok(())
217    /// # }
218    /// ```
219    pub async fn get_object_stream<S: AsRef<str>>(
220        &self,
221        path: S,
222    ) -> Result<ResponseDataStream, S3Error> {
223        let command = Command::GetObject;
224        let request = RequestImpl::new(self, path.as_ref(), command)?;
225        request.response_data_to_stream().await
226    }
227
228    /// Retrieve an S3 object list of tags.
229    ///
230    /// # Example:
231    ///
232    /// ```no_run
233    /// use s3::bucket::Bucket;
234    /// use s3::creds::Credentials;
235    /// use anyhow::Result;
236    ///
237    /// # #[tokio::main]
238    /// # async fn main() -> Result<()> {
239    ///
240    /// let bucket_name = "rust-s3-test";
241    /// let region = "us-east-1".parse()?;
242    /// let credentials = Credentials::default()?;
243    /// let bucket = Bucket::new(bucket_name, region, credentials)?;
244    ///
245    /// let response_data = bucket.get_object_tagging("/test.file").await?;
246    /// #
247    /// # Ok(())
248    /// # }
249    /// ```
250    pub async fn get_object_tagging<S: AsRef<str>>(
251        &self,
252        path: S,
253    ) -> Result<(Vec<crate::Tag>, u16), S3Error> {
254        let command = Command::GetObjectTagging {};
255        let request = RequestImpl::new(self, path.as_ref(), command)?;
256        let result = request.response_data(false).await?;
257
258        let mut tags = Vec::new();
259
260        if result.status_code() == 200 {
261            let result_string = String::from_utf8_lossy(result.as_slice());
262
263            // Add namespace if it doesn't exist
264            let ns = "http://s3.amazonaws.com/doc/2006-03-01/";
265            let result_string = if let Err(minidom::Error::MissingNamespace) =
266                result_string.parse::<minidom::Element>()
267            {
268                result_string
269                    .replace("<Tagging>", &format!("<Tagging xmlns=\"{}\">", ns))
270                    .into()
271            } else {
272                result_string
273            };
274
275            if let Ok(tagging) = result_string.parse::<minidom::Element>() {
276                for tag_set in tagging.children() {
277                    if tag_set.is("TagSet", ns) {
278                        for tag in tag_set.children() {
279                            if tag.is("Tag", ns) {
280                                let key = if let Some(element) = tag.get_child("Key", ns) {
281                                    element.text()
282                                } else {
283                                    "Could not parse Key from Tag".to_string()
284                                };
285                                let value = if let Some(element) = tag.get_child("Value", ns) {
286                                    element.text()
287                                } else {
288                                    "Could not parse Values from Tag".to_string()
289                                };
290                                tags.push(crate::Tag { key, value });
291                            }
292                        }
293                    }
294                }
295            }
296        }
297
298        Ok((tags, result.status_code()))
299    }
300}