Skip to content

Commit 4a7d4b3

Browse files
committed
Add streaming downloads with the stream_object Bucket method
Closes #10
1 parent 28dac36 commit 4a7d4b3

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rust-s3"
3-
version = "0.13.0"
3+
version = "0.14.0-beta.0"
44
authors = ["Drazen Urch", "Nick Stevens"]
55
description = "Tiny Rust library for working with Amazon S3."
66
repository = "https://github.com/durch/rust-s3"

src/bucket.rs

+27-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::mem;
33

44
use serde_xml;
55

6+
use std::io::Write;
67
use credentials::Credentials;
78
use command::Command;
89
use region::Region;
@@ -85,9 +86,33 @@ impl Bucket {
8586
request.execute()
8687
}
8788

89+
/// Stream file from S3 path to a local file, generic over T: Write.
90+
///
91+
/// # Example:
92+
///
93+
/// ```rust,no_run
94+
/// use s3::bucket::Bucket;
95+
/// use s3::credentials::Credentials;
96+
/// use std::fs::File;
97+
///
98+
/// let bucket_name = "rust-s3-test";
99+
/// let region = "us-east-1".parse().unwrap();
100+
/// let credentials = Credentials::default();
101+
/// let bucket = Bucket::new(bucket_name, region, credentials).unwrap();
102+
/// let mut output_file = File::create("output_file").expect("Unable to create file");
103+
///
104+
/// let code = bucket.stream_object("/test.file", &mut output_file).unwrap();
105+
/// println!("Code: {}", code);
106+
/// ```
107+
pub fn stream_object<T: Write>(&self, path: &str, writer: &mut T) -> S3Result<u32> {
108+
let command = Command::GetObject;
109+
let request = Request::new(self, path, command);
110+
request.stream(writer)
111+
}
112+
88113

89114
//// Get bucket location from S3
90-
////
115+
////
91116
/// # Example
92117
/// ```rust,no_run
93118
/// # // Fake credentials so we don't access user's real credentials in tests
@@ -376,7 +401,7 @@ impl Bucket {
376401

377402
/// Get a reference to the AWS token.
378403
pub fn token(&self) -> Option<&str> {
379-
self.credentials.token.as_ref().map(|s| s.as_str())
404+
self.credentials.token.as_ref().map(std::string::String::as_str)
380405
}
381406

382407
/// Get a reference to the full [`Credentials`](struct.Credentials.html)

src/request.rs

+23-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ extern crate base64;
22
extern crate md5;
33

44
use std::collections::HashMap;
5-
use std::io::Read;
5+
use std::io::{Read, Write};
66

77
use bucket::Bucket;
88
use chrono::{DateTime, Utc};
@@ -222,6 +222,16 @@ impl<'a> Request<'a> {
222222
}
223223

224224
pub fn execute(&self) -> S3Result<(Vec<u8>, u32)> {
225+
let response_tuple = self.maybe_stream::<Vec<u8>>(None)?;
226+
Ok((response_tuple.0.unwrap(), response_tuple.1))
227+
}
228+
229+
pub fn stream<T: Write>(&self, writer: &mut T) -> S3Result<u32> {
230+
let response_tuple = self.maybe_stream(Some(writer))?;
231+
Ok(response_tuple.1)
232+
}
233+
234+
pub fn maybe_stream<T: Write>(&self, writer: Option<&mut T>) -> S3Result<(Option<Vec<u8>>, u32)> {
225235
// TODO: preserve client across requests
226236
let client = if cfg!(feature = "no-verify-ssl") {
227237
reqwest::Client::builder()
@@ -250,13 +260,21 @@ impl<'a> Request<'a> {
250260
.body(content);
251261
let mut response = request.send()?;
252262

253-
// Read and process response
263+
let resp_code = u32::from(response.status().as_u16());
264+
265+
let ret;
254266
let mut dst = Vec::new();
255-
response.read_to_end(&mut dst)?;
256267

257-
let resp_code = u32::from(response.status().as_u16());
258268
if resp_code < 300 {
259-
Ok((dst, resp_code))
269+
// Read and process response
270+
if let Some(wrt) = writer {
271+
response.copy_to(wrt)?;
272+
ret = None;
273+
} else {
274+
response.copy_to(&mut dst)?;
275+
ret = Some(dst)
276+
}
277+
Ok((ret, resp_code))
260278
} else {
261279
let deserialized: AwsError = serde_xml::deserialize(dst.as_slice())?;
262280
let err = ErrorKind::AwsError {

0 commit comments

Comments
 (0)