File Uploads with Rails and Active Storage
- set up an account on Amazon Web Services to store uploaded images inside AWS S3
- set up rails app to handle file uploads
- understand the how and why, of a rails model with an image attribute
What is Active Storage
From the rails docs
Active Storage facilitates uploading files to a cloud storage service like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching those files to Active Record objects. It comes with a local disk-based service for development and testing and supports mirroring files to subordinate services for backups and migrations.
This is a big deal because before we had to use an external library (paperclip, carrier wave, etc) that facilitated these uploads. But now, rails does this by default, just using rails version
AWS Set up
- after we have set up our dependencies, we will need to set up our accounts on AWS. Let's do that!
- unfortunately, Amazon has changed their AWS sign up process from when I set up my original account, and we now have some additional hoops to jump through.
- first, navigate to this page: https://portal.aws.amazon.com/gp/aws/developer/registration/index.html?nc2=h_ct
- once there, it will ask you to enter your existing amazon information or to make a new account to use AWS
- from there, go through the sign up process. You will need to input your info, a credit card, and a phone number to verify. With the credit card option, select the
basicplan and you will not be charged
App Set up
- with our AWS account set up, let's continue with our code.
- for this example, we are going to make a new rails app inside your favorite workspace and name it what you would like
- for the purpose of this example, I am going to call mine,
- let's setup our app! run
rails new asset_upload -T --database=postgresqlinside your workspace where you want your repo
- once we have set up our app, let's
cdinto it, and open with
Now generally, from here, we would have to add some gems and other config files, but with using
rails 5.2.0, and getting the subsequent
Active Storage with it, we don't need to add anything additionally!
Getting our AWS Keys
Before we add to our config file, we need to get something called
Access Keys from AWS. Let's do that!
- navigate back to your AWS account and sign into the AWS Console (https://console.aws.amazon.com) if you don't already have it opened
- once signed in, find the All Services carot, then find
storage, and in that list, look for s3. once you find it, click on it.
- once inside our
S3section, let's create our first bucket. Find the blue
create bucketbutton and click on it
- when you click on it, it will pop up a modal with some info for your to fill out
- the name for our bucket must be unique, as it is shared across ALL OF AMAZON! try to find a unique name that you will remember
- for the region, select
US West N. California
- once sucessfully created, you will be brought to a page that will show you some info about your bucket. Next, lets grab our keys.
- hit this link https://console.aws.amazon.com/iam/home?nc2=h_m_sc#security_credential which will take you to the page for our credentials. On page load a modal will appear, just click
continue to security credentials
- once on that page, scroll down to the middle and click on
Access Keys (Access Key ID and Secret Access Key)and click
create new access key
- once that button is clicked, it will generate a file with your info and it will download it. Open it up. this will give you your access key, and your secret key
- once you have your access keys, make sure to remember a couple things before we dive into your code (write it down somewhere)
- your bucket name, and the region you made it in
- once on that page, scroll down to the middle and click on
With all that set up, lets now dive into our actual application. We're going to make a single table application, with a model called
user, that will have the following attributes
- name: string
- description: text
- images (will be seperate table)
to do this, we need to generate a blank migration file and model file, we can accomplish this with
rails g model User
now we have a blank migration file, so let's add some code to edit it to set up our database properly.
Inside of our migration let's add the following code so our
def change method looks like
def change create_table :users do |t| t.string :name t.string :description t.timestamps null: false end end
from here lets set up our database with the following command
rails db:create && rails db:migrate
With our basic application code set up, we need to now set up our active storage related code while it's fresh for us.
Components of Active Storage
In working with active storage, we need to only work with a couple files
First, let's add our active_storage tables. What these tables are, are where we will add all our image related data. To enable this feature, we just have to tell rails to do so. Go to your console, in your project folder, and run
Now, if you open your
schema.rb file, you should be able to see 3 tables:
This migration adds two tables -
active_storage_attachments. These are for the 2 models Blob and Attachment. Blob stores metadata like filename, content-type, byte size and checksum. The actual file is stored in the storage service or disk depending on your settings, which we will be changing.
A Blob can be attached to one or more Active Record objects through the Attachment join model, but we won't worry about that for now.
While we're in our schema/model/migration state of mind, let's add 1 line to our
user.rb file so that it looks like
class User < ApplicationRecord has_one_attached :image end
What this line is doing, is enabling
active_storage to work it's metaprogramming magic to map our image between the attachment table and the user table without us adding an image attribute to our users table. What this now means, is we have a method upon our user class called
image, which is responsible for rendering related images.
- with a model set up, lets now add some code to our controllers and our routes
- inside of our routes.rb file, let's delete all of the comments and just add the single line
- now for practice, let's manually create a users controller
- once we have our controller, let's just add a couple methods to our file like so
class UsersController < ApplicationController def new end def create end def show end end
- the final step is to now add our view!
- go inside of app/views and make a new folder called users. Inside users, create 2 files; a
- with all of this, let's fire up our server with rails s and navigate to http://localhost:3000
Adding credentials to our files
So we have our AWS keys set up, a base application w/
active_storage enabled, our model/controller/routes set up. Now, we just need to bring it all together by adding some more config code in the proper places to use active storage.
Modifying and encrypting files
The first thing we need to do, is to edit a new file rails gives us called
credentials.yml.enc. What this file does is to store all of our application secret's (like access keys and ids) and encrypts them for us. So grab your
rootkey.csv file downloaded from AWS and lets add our keys
- first open that file with
rails credentials:edit EDITOR='code --wait'
When that file opens up, you'll see
aws at the top. Uncomment them, and keep them spaced w/ 2 spaces (not 4) and don't use tabs. Once you have your
secret_access_key added, save the file, and exit out of it from your editor (you must exit it for changes to take place). Once your exit out of that file, you should see a message in your terminal (the window you just ran the command above in) that says
New credentials encrypted and saved.
Next, let's open up our
storage.yml file. In that file, find the
amazon option, and we should edit the file to look like
amazon: service: S3 access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> region: us-west-1 bucket: assetuploadexample
With our encrypted file, and our
storage.yml set up. Let's now open up
production.rb. Inside both of those files, let's add the following line (should be line 31 in
development.rb and line 42 in
production.rb) and add
config.active_storage.service = :amazon
This will tell rails to use the
amazon option inside our
The final thing here we need to add is the
aws gem to our gemfiles. So open up your
Gemfile and at the bottom add the line
Then go back to your terminal and run
Controllers and Views
Now that we have our base config set up, let's add the following to our controller and views. Inside of
UsersController lets add
class UsersController < ApplicationController def new @user = User.new end def create user = User.create! params.require(:user).permit(:name, :description) user.image.attach(params[:user][:image]) redirect_to user end def show @user = User.find(params[:id]) end def destroy end end
and inside of
<%= form_with model: @user, local: true do |form| %> <%= form.label :name%><br> <%= form.text_field :name %><br><br> <%= form.label :description%><br> <%= form.text_field :description %><br><br> <%= form.file_field :image %><br> <br><br> <%= form.submit %> <% end %>
and inside of
<p><%= @user.name %></p> <br> <p><%= @user.description %></p> <br> <%= image_tag @user.image %>
Now, go to your
localhost:3000/users/new and fill out the form. If everything goes well, we should be re-directed to
localhost:3000/users/1 and see an image there!
Now let's deploy this to production so we can briefly see a concept about something rails has called
master keys. Go to your terminal and run
This will generate a random url slug for your app. Next, let's find our app's master key. Go back to your text editor, and do a
command + p for a file called
master.key. Once you have that file, go back to your terminal and in your project directory, type
heroku config:set RAILS_MASTER_KEY=<master_key>where
master_keyis your string that was in that file (no brackets).
Next run the following commands to push to heroku
git add .
git commit -m "some commit"
git push heroku master && heroku open
Once heroku opens your application, go to
/users/new, and double check you are able to upload an image. If upon form submit you are redirected back to the show, and see an image, it worked!
With all of the code we currently have, get with your partner and add the rest of the code necessary to CRUD our resource!
- finding your AWS region key: https://docs.aws.amazon.com/general/latest/gr/rande.html
- active storage rails docs: http://edgeguides.rubyonrails.org/active_storage_overview.html