Using Amazon S3 to Store Files and Media for Your Heroku (Laravel) App
As a first-time user of Heroku, I was really confused as to why file uploads and symbolic links do not work on a deployed application. After some searching I learned about Heroku’s dyno and its ephemeral file-system. Basically, whatever changes I have made to the file-system will be discarded when the application is redeployed (occurs approximately once a day). Heroku recommends instead, backing services such as the Amazon S3 for file and media storage. This is good because Laravel is very “S3-friendly” and it just makes things much easier. Let us get straight into it.
Before we begin, here is a list of important things we need to know (for this particular use case: a product catalog where admin can upload product images that users can see).
- We will need AWS credentials to manipulate objects and folders within the bucket.
- The bucket will be publicly available to reads for ease of access (functions similar to the public folder).
First, we need to create an S3 bucket to store files and media for our application. In the console, go to Services (at the top of the console) and click on S3 under the Storage section. At the S3 page, click Create Bucket and follow the instructions. Since we are just creating a bucket to store some files, we can leave all options at default EXCEPT the bucket name and region. (Note: Remember to turn off Block public access if it happens to be be turned on.)
When we want to access the images we saved in the bucket (which is every single time), it will be less tedious if we can just get them without providing access credentials every time. However, exposing our bucket is extremely unsafe. Hence, we need to create a policy, or “bucket policy” which allows public access to reads only. Go ahead and click on a bucket.
At our bucket page, go to the Permissions section and click on Bucket Policy to bring out an editor. Enter the following code to create a bucket policy that will allow us to get the objects from the buckets (replace “bucket_name”).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::bucket_name/*"
}
]
}
Click save and now we are almost done on the AWS side.
Finally, we need to retrieve our AWS credentials so we can access our bucket and upload files to it. Click on your AWS account and in the drop-down select My Security Credentials. Under Access Keys, click Create New Access Key and take note of the new Access Key ID and Secret Access Key because we will need them later.
Next, we will configure some settings for our Heroku application. Visit the Heroku dashboard and click on your application to view its settings. Under Settings, we will see Config Vars with a button that says Reveal Config Vars. Click Reveal Config Vars and add these key-value pairs.
- AWS_ACCESS_KEY_ID (key) and its value.
- AWS_SECRET_ACCESS_KEY (key) and its value.
- AWS_BUCKET (key) and your bucket’s name.
- AWS_DEFAULT_REGION and your bucket’s set region.
And we are done setting up.
Since we have already set up the environment variables via Heroku (which is safer than keeping them in your repository), all that is left is to configure our code to upload files to the bucket and learn how to retrieve them. Storing a file to a bucket is no different than storing it elsewhere and there is more than one way to do this. In my case, I used the following line.
Storage::disk('s3')->put($filename, file_get_contents($file));
As for retrieval, simply refer to the objects’ URLs from our bucket. Go to your bucket, click on a file to see its object URL. The root path is constant and you will only need to change the end portion of the URL. For example, an “img” element will set its “src” attribute as “https://bucket_name.s3-ap-southeast-1.amazonaws.com/images/file_name.jpg”.
And that is how to use Amazon S3 to store files and media for a Heroku Laravel application.