Skip to main content

AWS SDK for Rust

The AWS SDK for Rust is generally available and works with Filebase via endpoint_url and region.

Install

Cargo.toml
[dependencies]
aws-config = { version = "1", features = ["behavior-version-latest"] }
aws-sdk-s3 = "1"
aws-credential-types = "1"
tokio = { version = "1", features = ["full"] }

Initialize the client

src/filebase.rs
use aws_config::BehaviorVersion;
use aws_credential_types::Credentials;
use aws_sdk_s3::{Client, Config};

pub async fn new_client() -> Client {
let creds = Credentials::new(
std::env::var("FILEBASE_KEY").unwrap(),
std::env::var("FILEBASE_SECRET").unwrap(),
None,
None,
"filebase",
);

let cfg = Config::builder()
.behavior_version(BehaviorVersion::latest())
.endpoint_url("https://s3.filebase.io")
.region(aws_sdk_s3::config::Region::new("auto"))
.credentials_provider(creds)
.build();

Client::from_conf(cfg)
}

List buckets

let client = new_client().await;

let resp = client.list_buckets().send().await?;

for b in resp.buckets() {
println!("{}", b.name().unwrap_or_default());
}

Create a bucket

client
.create_bucket()
.bucket("my-bucket")
.send()
.await?;

Upload an object

use aws_sdk_s3::primitives::ByteStream;

let body = ByteStream::from_path("photo.jpg").await?;

client
.put_object()
.bucket("my-bucket")
.key("photo.jpg")
.body(body)
.content_type("image/jpeg")
.cache_control("public, max-age=31536000")
.send()
.await?;

For large files, drive multipart upload directly:

let create = client
.create_multipart_upload()
.bucket("my-bucket")
.key("video.mp4")
.send()
.await?;

let upload_id = create.upload_id().unwrap();

// Upload parts in a loop, collecting CompletedPart values...
// Then:
client
.complete_multipart_upload()
.bucket("my-bucket")
.key("video.mp4")
.upload_id(upload_id)
.multipart_upload(/* CompletedMultipartUpload */)
.send()
.await?;

List objects

let mut continuation_token: Option<String> = None;

loop {
let req = client
.list_objects_v2()
.bucket("my-bucket")
.prefix("photos/")
.set_continuation_token(continuation_token.clone());

let resp = req.send().await?;

for obj in resp.contents() {
println!("{} {}", obj.key().unwrap_or_default(), obj.size().unwrap_or(0));
}

if !resp.is_truncated().unwrap_or(false) {
break;
}
continuation_token = resp.next_continuation_token().map(String::from);
}

Download an object

use tokio::io::AsyncWriteExt;

let resp = client
.get_object()
.bucket("my-bucket")
.key("photo.jpg")
.send()
.await?;

let bytes = resp.body.collect().await?.into_bytes();

let mut file = tokio::fs::File::create("downloaded.jpg").await?;
file.write_all(&bytes).await?;

Delete an object

client
.delete_object()
.bucket("my-bucket")
.key("photo.jpg")
.send()
.await?;

Pre-signed URLs

use aws_sdk_s3::presigning::PresigningConfig;
use std::time::Duration;

let presigning_config = PresigningConfig::expires_in(Duration::from_secs(3600))?;

let req = client
.get_object()
.bucket("my-bucket")
.key("private.pdf")
.presigned(presigning_config)
.await?;

println!("{}", req.uri());

Error handling

use aws_sdk_s3::error::SdkError;
use aws_sdk_s3::operation::head_object::HeadObjectError;

match client.head_object().bucket("my-bucket").key("maybe.jpg").send().await {
Ok(_) => println!("exists"),
Err(SdkError::ServiceError(err)) => {
match err.err() {
HeadObjectError::NotFound(_) => println!("not found"),
other => return Err(other.clone().into()),
}
}
Err(e) => return Err(e.into()),
}

What's next