For this next iteration, I’ve switched from a SNS and added a presigned URL for download in the email. With the SQS this was hard for the customer to read; switching over to a SES created email makes it much simpler and quicker to understand; they know the name of the file and the folder it’s been uploaded to. I’ve included a pre-signed URL, this way they can download the file right from the email. They will still need to go into the S3 bucket (via Cyberduck) to delete the file or wait for the auto delete to happen; leaving till auto delete will incur extra costs.
The basic code for the SES:
import os
import boto3
from urllib.parse import unquote_plus
s3_client = boto3.client('s3')
ses_client = boto3.client('ses', region_name='us-east-1') # Change region as needed
SENDER = "sender@example.com" # Must be verified in SES
RECIPIENT = "recipient@example.com, email@email.com" # Must be verified in SES (unless SES is out of sandbox)
EXPIRATION = 604800, # 7 days
def lambda_handler(event, context):
# Get bucket and object key from the event
bucket = event['Records'][0]['s3']['bucket']['name']
key = unquote_plus(event['Records'][0]['s3']['object']['key'])
# Generate presigned URL
presigned_url = s3_client.generate_presigned_url(
'get_object',
Params={'Bucket': bucket, 'Key': key},
ExpiresIn=EXPIRATION
)
# Compose email
subject = "Your file is ready for download"
body_text = f"Your file has been uploaded. Download it here (valid for 7 days:\n{presigned_url}"
# Send email via SES
response = ses_client.send_email(
Source=SENDER,
Destination={'ToAddresses': [RECIPIENT]},
Message={
'Subject': {'Data': subject},
'Body': {'Text': {'Data': body_text}}
}
)
return {
'statusCode': 200,
'body': 'Email sent!'
}
Iteration 1 - https://www.lynnamacher.com/setting-up-s3-like-an-ftp-site/
Iteration 2 - https://www.lynnamacher.com/s3-with-cyberduck-iteration-2/


