Store File Attachments in Amazon S3 in Learn to Code Rails App


In this lesson we're going to learn how to use Amazon Web Services S3 buckets to hold the video files uploaded to our application. Right now they're being stored on our local machine, but if that were the case on our production server and we had any traffic we would run out of room pretty quickly.

The first step in integrating AWS S3 storage into our application is installing the gem. Open your Gemfile and add the following line:

gem 'aws-sdk-s3', require: false

Any time we add a new gem we need to make sure to bundle it:

bundle install

The next thing we need to do is set up things on Amazon's end. Things stored using S3 are stored in buckets. Buckets are just a way to organize our data. Before we can get around to creating a bucket we'll need to create an account. Once we've created our account and landed on the AWS Console there should be a link to the S3 service. Once you're at the S3 dashboard just create a new bucket. Let's name our bucket appropriately. In my case I'm going to go with nimblehq-content. For the rest of the bucket options let's just all defaults.

Now we have a bucket set up but our application need's access to it via an AWS user's credentials. To create this user and retrieve their credentials we need to visit the IAM module of the AWS console. In the left sidebar there is a Users option. Navigate to it and then Add user.

For the user name I'm going to be using nimblehq-app but feel free to name your user appropriately if you've named your application something different. We need to make sure we give our user programmatic access. Next, under permissions, select Attach exisiting policies directly and use S3 to filter the policies. Grant the user access to the AmazonS3FullAccess policy. Don't worry about tags for now and just skip to review and create. Now we have a user that will act as a set of credentials we can use in our Rails app to access our AWS S3 bucket.

Upon creating our user we will be redirected to a screen displaying our User, Access key ID and Secret access key. Under no circumstances should you ever share your Secret access key. These are the credentials that will let our application talk to the S3 bucket.

Let's head to our application's root/config/storage.yml file and make some changes. The boilerplate code needed for our application to talk to S3 is actually already here, we just need to uncomment it. It's the Amazon segment of the storage.yml file. Our service will be S3  and the rest of the attributes we will fill in with the AWS user credentials. We don't want to push our AWS credentials to Github with our code base, though, so we're going to need to use environment variables.

Create environment variables for our bucket name, secret key and access ID. Remember to format your code for embedded Ruby when calling the environment variable:

   service: S3
   access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
   secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
   region: us-east-1
   bucket: <%= ENV['AWS_BUCKET'] %>

The next thing we need to is go to our applications root/config/environments/production.rb file and find the following line of code:

config.active_storage.service = :local

We want to change this line of code to tell the production environment . to use the Amazon configuration we just set up in our storage.yml file. To do that we just change the symbol:

config.active_storage.service = :amazon

Now our application will store files uploaded in production to our S3 bucket, but files uploaded when the application is running in development will be stored in our local storage.

The problem this creates is that the environment variables we used in the storage.yml file aren't defined on our Heroku environment (our production application's server). To do this we need to head to Heroku and sign in. Once at your dashboard, select your application and go to the settings. Once in settings, we need to click reveal config vars. Here is where we define the environment variables. Remember to make sure all your Heroku config variables match the names you used in the storage.yml file.

We've defined our environment variables and configured our application to store files to a S3 bucket in production. We won't be able to test this until we make another deploy to our Heroku server. We will do so soon but until then...

Happy coding!!!