Using rusoto to upload a file to S3
A while ago I made a Python script to upload files to an S3 bucket. It's a simple script that uses the boto3
library to interact with the AWS API. Check it out here.
Let's see how to do this with Rust and the rusoto
crate.
First, I added the following dependencies to my Cargo.toml
file:
...
[dependencies]
clap = { version = "4.5.7", features = ["derive", "env"] }
rusoto_core = "0.48.0"
rusoto_s3 = "0.48.0"
tokio = { version = "1.38.0", features = ["full"] }
This is what I came up with for the first iteration:
use rusoto_core::{Region, credential::EnvironmentProvider, HttpClient};
use rusoto_s3::{S3Client, S3, PutObjectRequest};
use tokio::fs::File;
use tokio::io::AsyncReadExt;
use std::error::Error;
use clap::Parser;
use std::path::Path;
#[derive(Parser, Debug)]
#[clap(about, version, author)]
struct Args {
#[clap(short, long, env = "S3_BUCKET_NAME")]
bucket: String,
#[clap(short, long, env = "AWS_REGION")]
region: String,
#[clap(short, long)]
file: String,
}
async fn upload_image_to_s3(bucket: &str, key: &str, file: &str, region: Region) -> Result<(), Box<dyn Error>> {
let s3_client = S3Client::new_with(HttpClient::new()?, EnvironmentProvider::default(), region);
let mut file = File::open(file).await?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer).await?;
let put_request = PutObjectRequest {
bucket: bucket.to_string(),
key: key.to_string(),
body: Some(buffer.into()),
..Default::default()
};
s3_client.put_object(put_request).await?;
println!("File uploaded successfully to {}/{}", bucket, key);
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let args = Args::parse();
let key = Path::new(&args.file)
.file_name()
.and_then(|name| name.to_str())
.ok_or("Invalid file path")?;
let region = args.region.parse::<Region>()?;
upload_image_to_s3(&args.bucket, key, &args.file, region).await?;
Ok(())
}
- I use Clap (as usual) for handling command-line arguments.
- I use the
rusoto_core
andrusoto_s3
crates to interact with the AWS API. - I use the
tokio
crate to make it asynchronous. - The
upload_image_to_s3
function reads the file, creates aPutObjectRequest
, and uploads the file to the specified S3 bucket. - The
main
function parses the command-line arguments, extracts the file name, and uploads the file to S3.
To run the program, you need to set the AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
environment variables:
$ export AWS_ACCESS_KEY_ID=your_access_key_id
$ export AWS_SECRET_ACCESS_KEY=your_secret_access_key
And either specify region and bucket from the command line or set the S3_BUCKET_NAME
and AWS_REGION
environment variables if you always want to use the same bucket and region:
$ export S3_BUCKET_NAME=example-bucket
$ export AWS_REGION=us-east-2
With all this set you only need to give the file you want to upload:
$ cargo run -- -f ~/Desktop/cat.png
...
File uploaded successfully to example-bucket/cat.png
Conclusion
And that's it, a Rust utitlity to upload files to S3. 🚀
I extended the program a bit to allow multiple files to be uploaded and list the images, including with pagination so I can use it later with HTMX' infinite scroll. I will write about that in a future post ...
The repo is here and I pushed it to crates.io as well. 🦀