From 0735fd0e991674b24b7692fd8da262f844888b70 Mon Sep 17 00:00:00 2001 From: helloimalastair Date: Wed, 19 Mar 2025 14:51:52 -0700 Subject: [PATCH 1/2] R2-2800: document aws-sdk-s3 rust crate for r2 --- .../docs/r2/examples/aws/aws-sdk-rust.mdx | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/content/docs/r2/examples/aws/aws-sdk-rust.mdx diff --git a/src/content/docs/r2/examples/aws/aws-sdk-rust.mdx b/src/content/docs/r2/examples/aws/aws-sdk-rust.mdx new file mode 100644 index 000000000000000..6dbff06a89e6d5e --- /dev/null +++ b/src/content/docs/r2/examples/aws/aws-sdk-rust.mdx @@ -0,0 +1,209 @@ +--- +title: aws-sdk-rust +pcx_content_type: example +--- + +import { Render } from "~/components"; + + +
+ +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: + +:::note[Compatibility] +Client versions may introduce modifications to the default checksum behavior that could be incompatible with R2 APIs. + +If you encounter checksum-related errors, add the following to your config: + +```rust +.request_checksum_calculation(RequestChecksumCalculation::WhenRequired) +.response_checksum_validation(ResponseChecksumValidation::WhenRequired) +``` + +::: + +## Basic Usage + +```rust +use aws_sdk_s3 as s3; +use aws_smithy_types::date_time::Format::DateTime; + +#[tokio::main] +async fn main() -> Result<(), s3::Error> { + let bucket_name = "sdk-example"; + let account_id = ""; + let access_key_id = ""; + let access_key_secret = ""; + + // Configure the client + let config = aws_config::from_env() + .endpoint_url(format!("https://{}.r2.cloudflarestorage.com", account_id)) + .credentials_provider(aws_sdk_s3::config::Credentials::new( + access_key_id, + access_key_secret, + None, // session token is not used with R2 + None, + "R2", + )) + .region("auto") + .load() + .await; + + let client = s3::Client::new(&config); + + // List buckets + let list_buckets_output = client.list_buckets().send().await?; + + println!("Buckets:"); + for bucket in list_buckets_output.buckets() { + println!(" - {}: {}", + bucket.name().unwrap_or_default(), + bucket.creation_date().map_or_else( + || "Unknown creation date".to_string(), + |date| date.fmt(DateTime).unwrap() + ) + ); + } + + // List objects in a specific bucket + let list_objects_output = client + .list_objects_v2() + .bucket(bucket_name) + .send() + .await?; + + println!("\nObjects in {}:", bucket_name); + for object in list_objects_output.contents() { + println!(" - {}: {} bytes, last modified: {}", + object.key().unwrap_or_default(), + object.size().unwrap_or_default(), + object.last_modified().map_or_else( + || "Unknown".to_string(), + |date| date.fmt(DateTime).unwrap() + ) + ); + } + + Ok(()) +} +``` + +## Uploading Objects + +To upload an object to R2: + +```rust +use aws_sdk_s3::primitives::ByteStream; +use std::path::Path; + +async fn upload_object( + client: &s3::Client, + bucket: &str, + key: &str, + file_path: &str, +) -> Result<(), s3::Error> { + let body = ByteStream::from_path(Path::new(file_path)).await.unwrap(); + + client + .put_object() + .bucket(bucket) + .key(key) + .body(body) + .send() + .await?; + + println!("Uploaded {} to {}/{}", file_path, bucket, key); + Ok(()) +} +``` + +## Downloading Objects + +To download an object from R2: + +```rust +use std::fs; +use std::io::Write; + +async fn download_object( + client: &s3::Client, + bucket: &str, + key: &str, + output_path: &str, +) -> Result<(), Box> { + let resp = client + .get_object() + .bucket(bucket) + .key(key) + .send() + .await?; + + let data = resp.body.collect().await?; + let bytes = data.into_bytes(); + + let mut file = fs::File::create(output_path)?; + file.write_all(&bytes)?; + + println!("Downloaded {}/{} to {}", bucket, key, output_path); + Ok(()) +} +``` + +## Generate Presigned URLs + +You can also generate presigned links that can be used to temporarily share public read or write access to a bucket. + +```rust +use aws_sdk_s3::presigning::PresigningConfig; +use std::time::Duration; + +async fn generate_get_presigned_url( + client: &s3::Client, + bucket: &str, + key: &str, + expires_in: Duration, +) -> Result { + let presigning_config = PresigningConfig::expires_in(expires_in)?; + + // Generate a presigned URL for GET (download) + let presigned_get_request = client + .get_object() + .bucket(bucket) + .key(key) + .presigned(presigning_config) + .await?; + + Ok(presigned_get_request.uri().to_string()) +} + +async fn generate_upload_presigned_url( + client: &s3::Client, + bucket: &str, + key: &str, + expires_in: Duration, +) -> Result { + let presigning_config = PresigningConfig::expires_in(expires_in)?; + + // Generate a presigned URL for PUT (upload) + let presigned_put_request = client + .put_object() + .bucket(bucket) + .key(key) + .presigned(presigning_config) + .await?; + + Ok(presigned_put_request.uri().to_string()) +} +``` + +You can use these presigned URLs with any HTTP client. For example, to upload a file using the PUT URL: + +```bash +curl -X PUT "https://" -H "Content-Type: application/octet-stream" --data-binary "@local-file.txt" +``` + +To download a file using the GET URL: + +```bash +curl -X GET "https://" -o downloaded-file.txt +``` \ No newline at end of file From c1709b938015799589004d9e40d3011c84001c65 Mon Sep 17 00:00:00 2001 From: Jun Lee Date: Mon, 24 Mar 2025 18:03:21 +0000 Subject: [PATCH 2/2] Apply suggestions from code review --- src/content/docs/r2/examples/aws/aws-sdk-rust.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/r2/examples/aws/aws-sdk-rust.mdx b/src/content/docs/r2/examples/aws/aws-sdk-rust.mdx index 6dbff06a89e6d5e..a1f3df3923b4c6d 100644 --- a/src/content/docs/r2/examples/aws/aws-sdk-rust.mdx +++ b/src/content/docs/r2/examples/aws/aws-sdk-rust.mdx @@ -88,7 +88,7 @@ async fn main() -> Result<(), s3::Error> { } ``` -## Uploading Objects +## Upload Objects To upload an object to R2: @@ -117,7 +117,7 @@ async fn upload_object( } ``` -## Downloading Objects +## Download Objects To download an object from R2: