Skip to content

Instantly share code, notes, and snippets.

@toddlers
Last active February 15, 2024 17:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save toddlers/2b300e007d2bfbfa5f152f3dba1cfb22 to your computer and use it in GitHub Desktop.
Save toddlers/2b300e007d2bfbfa5f152f3dba1cfb22 to your computer and use it in GitHub Desktop.
s3 presigned urls
[package]
name = "s3_presign"
version = "0.1.0"
edition = "2021"
# Starting in Rust 1.62 you can use `cargo add` to add dependencies
# to your project.
#
# If you're using an older Rust version,
# download cargo-edit(https://github.com/killercup/cargo-edit#installation)
# to install the `add` subcommand.
#
# Running `cargo add DEPENDENCY_NAME` will
# add the latest version of a dependency to the list,
# and it will keep the alphabetic ordering for you.
[dependencies]
aws-config = "1.1.5"
aws-sdk-s3 = "1.15.0"
aws_lambda_events = { version = "0.14.0", default-features = false, features = ["cloudwatch_events"] }
lambda_runtime = "0.9.2"
serde = "1.0.196"
tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["env-filter", "fmt"] }
// lambda to generate a presigned url for an s3 object
use aws_config::meta::region::RegionProviderChain;
use aws_config::BehaviorVersion;
use aws_sdk_s3::presigning::PresigningConfig;
use aws_sdk_s3::Client as S3Client;
use aws_sdk_s3::{config::Region, meta::PKG_VERSION, Client};
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
#[derive(Debug, Deserialize)]
struct Request {
/// name of the bucket
bucket: String,
/// The object key
object: String,
/// lifetime
expires_in: Option<u64>,
/// verbose
verbose: bool,
/// region
region_name: Option<String>,
}
#[derive(Serialize)]
struct Response {
req_id: String,
uri: String,
}
async fn put_object(event: LambdaEvent<Request>) -> Result<Response, Error> {
let client = create_s3_client(&event).await.unwrap();
let expires_in = Duration::from_secs(event.payload.expires_in.unwrap_or(900));
let bucket_name = &event.payload.bucket;
let object_name = &event.payload.object;
let presigned_request = client
.get_object()
.bucket(bucket_name)
.key(object_name)
.presigned(PresigningConfig::expires_in(expires_in)?)
.await?;
println!("Object URI: {}", presigned_request.uri());
let rsp = Response {
req_id: event.context.request_id,
uri: presigned_request.uri().to_string(),
};
println!();
if event.payload.verbose {
println!("S3 client version: {}", PKG_VERSION);
println!("Bucket: {}", &bucket_name);
println!("Object: {}", &object_name);
println!("Expires in: {:?} seconds", expires_in);
println!();
}
Ok(rsp)
}
// Function to create the S3 client
async fn create_s3_client(event: &LambdaEvent<Request>) -> Result<S3Client, String> {
let region_name = event.payload.region_name.clone();
let region_provider = RegionProviderChain::first_try(region_name.map(Region::new))
.or_default_provider()
.or_else(Region::new("eu-west-1"));
let shared_config = aws_config::defaults(BehaviorVersion::v2023_11_09())
.region(region_provider)
.load()
.await;
println!("Region: {}", shared_config.region().unwrap());
let client = Client::new(&shared_config);
Ok(client)
}
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.with_target(false)
.without_time()
.init();
let func =
service_fn(move |event: LambdaEvent<Request>| async move { put_object(event).await });
run(func).await?;
Ok(())
}
use aws_config::BehaviorVersion;
use aws_sdk_s3::presigning::PresigningConfig;
use aws_sdk_s3::Client as S3Client;
use lambda_runtime::{run, service_fn, Error, LambdaEvent};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tracing_subscriber::filter::{EnvFilter, LevelFilter};
#[derive(Debug, Deserialize)]
struct Request {
/// name of the bucket
bucket: String,
/// The object key
object: String,
/// lifetime
expires_in: Option<u64>,
/// verbose
verbose: bool,
}
#[derive(Serialize)]
struct Response {
req_id: String,
uri: String,
}
async fn put_object(
event: LambdaEvent<Request>,
client: &S3Client,
) -> Result<Response, Error> {
let expires_in = Duration::from_secs(event.payload.expires_in.unwrap_or(900));
let bucket_name = &event.payload.bucket;
let object_name = &event.payload.object;
let presigned_request = client
.put_object()
.bucket(bucket_name)
.key(object_name)
.presigned(PresigningConfig::expires_in(expires_in)?)
.await?;
println!("Object URI: {}", presigned_request.uri());
let rsp = Response {
req_id: event.context.request_id,
uri: presigned_request.uri().to_string(),
};
println!();
if event.payload.verbose {
println!("Bucket: {}", bucket_name);
println!("Object: {}", object_name);
println!("Expires in: {:?} seconds", expires_in);
println!();
}
Ok(rsp)
}
#[tokio::main]
async fn main() -> Result<(), Error> {
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.with_target(false)
.without_time()
.init();
let shared_config = aws_config::load_defaults(BehaviorVersion::v2023_11_09()).await;
let s3_client = S3Client::new(&shared_config);
let client_ref = &s3_client;
let func = service_fn(move |event: LambdaEvent<Request>| async move { put_object(event, client_ref).await });
println!("Region: {}", shared_config.region().unwrap());
run(func).await?;
Ok(())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment