A big feature I contributed to our project is the ability to upload an image. This required deploying some AWS infrastructure, configuring the frontend to select a file and convert it to base64 and send it to the API, and configuring the API to upload the image to S3 and save the image URL and path name in the database.
This was my first time doing something with images like this so it required doing some research. I read blog posts about this and decided a good first step would be to figure out how to convert a file uploaded from the computer to the browser into a base64 encoded image. It turned out that doing this conversion was pretty easy and the FileReader JavaScript object is standard and basically did most of this for us.
The next step in this process was decoding the base64 image inside of the lambda function. This was also easy thanks to the base64 python module. An interesting caveat is that the image file might contain a string at the beginning describing the content type, if that is the case the description needs to be striped off before decoding. Next, the image is uploaded into an S3 bucket. This bucket is different than the bucket that stores our React application so now our application contains two S3 buckets.
Before uploading the image into the S3 bucket there is a timestamp generated. This makes it so each uploaded image is unique even if they share the same name. Now that the image is in the bucket it can be stored inside of our database. Since we are using CloudFront as a content distributor we need to store the URL to the image in CloudFront. However, we also need to store the path to the image so it can be deleted later.
To solve this issue I passed the CloudFront distribution domain name into the lambda function during deployment as an environmental variable. When saving the image in the database I store both the formatted URL to the image, as well as a path name of the image in the S3 bucket. This is important because now when someone deletes an item it can be deleted from the database as well as the S3 bucket. If we didn’t delete the image from the bucket there would be orphaned images in the bucket consuming storage. I’m sure there are other ways to do this but it seems to be working for us.
A simpler way to do this would be to have the browser directly load the images from S3 and not use CloudFront. However, CloudFront provides some important benefits in performance and cost in comparison to directly getting the images from the S3 bucket.
First I will discuss the performance benefits to using CloudFront. CloudFront is a global content delivery network (CDN). This service peers with thousands of telecom carriers globally and has over 410 points of presence in 90+ cities in 48 countries. This means our images are cached in every continent (except Antartica) and will be close to whoever is viewing them.
The cost savings of CloudFront are quite significant for an application that is distributing a lot of static content. Data transfer to the internet in the United States (for the highest use tier of pricing) costs $0.05 per GB with S3 and $0.02 per GB with CloudFront. This is a 60% savings in data transfer to the internet. Data transfer from S3 to CloudFront is free.
A great part of the CloudFront pricing is the free tier. For S3 the free tier expires after 12 months. However, for CloudFront, the free tier is forever. The free tier for CloudFront includes 1 TB of data transferred to the internet per month. The second terabyte of data transferred to the internet costs $85. That means that Amazon is providing a free $85 per month with this service, this is higher than any other free tier that I know of on AWS.