Skip to content

Commit 7e23677

Browse files
R2-2800: Document aws-sdk-s3 rust crate for R2 (#20973)
* R2-2800: document aws-sdk-s3 rust crate for r2 * Apply suggestions from code review --------- Co-authored-by: helloimalastair <[email protected]> Co-authored-by: Jun Lee <[email protected]>
1 parent b184f0d commit 7e23677

File tree

1 file changed

+209
-0
lines changed

1 file changed

+209
-0
lines changed
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
title: aws-sdk-rust
3+
pcx_content_type: example
4+
---
5+
6+
import { Render } from "~/components";
7+
8+
<Render file="keys" />
9+
<br />
10+
11+
This example uses the [aws-sdk-s3](https://crates.io/crates/aws-sdk-s3) crate from the [AWS SDK for Rust](https://github.com/awslabs/aws-sdk-rust). You must pass in the R2 configuration credentials when instantiating your `S3` client:
12+
13+
:::note[Compatibility]
14+
Client versions may introduce modifications to the default checksum behavior that could be incompatible with R2 APIs.
15+
16+
If you encounter checksum-related errors, add the following to your config:
17+
18+
```rust
19+
.request_checksum_calculation(RequestChecksumCalculation::WhenRequired)
20+
.response_checksum_validation(ResponseChecksumValidation::WhenRequired)
21+
```
22+
23+
:::
24+
25+
## Basic Usage
26+
27+
```rust
28+
use aws_sdk_s3 as s3;
29+
use aws_smithy_types::date_time::Format::DateTime;
30+
31+
#[tokio::main]
32+
async fn main() -> Result<(), s3::Error> {
33+
let bucket_name = "sdk-example";
34+
let account_id = "<accountid>";
35+
let access_key_id = "<access_key_id>";
36+
let access_key_secret = "<access_key_secret>";
37+
38+
// Configure the client
39+
let config = aws_config::from_env()
40+
.endpoint_url(format!("https://{}.r2.cloudflarestorage.com", account_id))
41+
.credentials_provider(aws_sdk_s3::config::Credentials::new(
42+
access_key_id,
43+
access_key_secret,
44+
None, // session token is not used with R2
45+
None,
46+
"R2",
47+
))
48+
.region("auto")
49+
.load()
50+
.await;
51+
52+
let client = s3::Client::new(&config);
53+
54+
// List buckets
55+
let list_buckets_output = client.list_buckets().send().await?;
56+
57+
println!("Buckets:");
58+
for bucket in list_buckets_output.buckets() {
59+
println!(" - {}: {}",
60+
bucket.name().unwrap_or_default(),
61+
bucket.creation_date().map_or_else(
62+
|| "Unknown creation date".to_string(),
63+
|date| date.fmt(DateTime).unwrap()
64+
)
65+
);
66+
}
67+
68+
// List objects in a specific bucket
69+
let list_objects_output = client
70+
.list_objects_v2()
71+
.bucket(bucket_name)
72+
.send()
73+
.await?;
74+
75+
println!("\nObjects in {}:", bucket_name);
76+
for object in list_objects_output.contents() {
77+
println!(" - {}: {} bytes, last modified: {}",
78+
object.key().unwrap_or_default(),
79+
object.size().unwrap_or_default(),
80+
object.last_modified().map_or_else(
81+
|| "Unknown".to_string(),
82+
|date| date.fmt(DateTime).unwrap()
83+
)
84+
);
85+
}
86+
87+
Ok(())
88+
}
89+
```
90+
91+
## Upload Objects
92+
93+
To upload an object to R2:
94+
95+
```rust
96+
use aws_sdk_s3::primitives::ByteStream;
97+
use std::path::Path;
98+
99+
async fn upload_object(
100+
client: &s3::Client,
101+
bucket: &str,
102+
key: &str,
103+
file_path: &str,
104+
) -> Result<(), s3::Error> {
105+
let body = ByteStream::from_path(Path::new(file_path)).await.unwrap();
106+
107+
client
108+
.put_object()
109+
.bucket(bucket)
110+
.key(key)
111+
.body(body)
112+
.send()
113+
.await?;
114+
115+
println!("Uploaded {} to {}/{}", file_path, bucket, key);
116+
Ok(())
117+
}
118+
```
119+
120+
## Download Objects
121+
122+
To download an object from R2:
123+
124+
```rust
125+
use std::fs;
126+
use std::io::Write;
127+
128+
async fn download_object(
129+
client: &s3::Client,
130+
bucket: &str,
131+
key: &str,
132+
output_path: &str,
133+
) -> Result<(), Box<dyn std::error::Error>> {
134+
let resp = client
135+
.get_object()
136+
.bucket(bucket)
137+
.key(key)
138+
.send()
139+
.await?;
140+
141+
let data = resp.body.collect().await?;
142+
let bytes = data.into_bytes();
143+
144+
let mut file = fs::File::create(output_path)?;
145+
file.write_all(&bytes)?;
146+
147+
println!("Downloaded {}/{} to {}", bucket, key, output_path);
148+
Ok(())
149+
}
150+
```
151+
152+
## Generate Presigned URLs
153+
154+
You can also generate presigned links that can be used to temporarily share public read or write access to a bucket.
155+
156+
```rust
157+
use aws_sdk_s3::presigning::PresigningConfig;
158+
use std::time::Duration;
159+
160+
async fn generate_get_presigned_url(
161+
client: &s3::Client,
162+
bucket: &str,
163+
key: &str,
164+
expires_in: Duration,
165+
) -> Result<String, s3::Error> {
166+
let presigning_config = PresigningConfig::expires_in(expires_in)?;
167+
168+
// Generate a presigned URL for GET (download)
169+
let presigned_get_request = client
170+
.get_object()
171+
.bucket(bucket)
172+
.key(key)
173+
.presigned(presigning_config)
174+
.await?;
175+
176+
Ok(presigned_get_request.uri().to_string())
177+
}
178+
179+
async fn generate_upload_presigned_url(
180+
client: &s3::Client,
181+
bucket: &str,
182+
key: &str,
183+
expires_in: Duration,
184+
) -> Result<String, s3::Error> {
185+
let presigning_config = PresigningConfig::expires_in(expires_in)?;
186+
187+
// Generate a presigned URL for PUT (upload)
188+
let presigned_put_request = client
189+
.put_object()
190+
.bucket(bucket)
191+
.key(key)
192+
.presigned(presigning_config)
193+
.await?;
194+
195+
Ok(presigned_put_request.uri().to_string())
196+
}
197+
```
198+
199+
You can use these presigned URLs with any HTTP client. For example, to upload a file using the PUT URL:
200+
201+
```bash
202+
curl -X PUT "https://<your-presigned-put-url>" -H "Content-Type: application/octet-stream" --data-binary "@local-file.txt"
203+
```
204+
205+
To download a file using the GET URL:
206+
207+
```bash
208+
curl -X GET "https://<your-presigned-get-url>" -o downloaded-file.txt
209+
```

0 commit comments

Comments
 (0)