- 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
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 5.2.0
!
- 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
basic
plan and you will not be charged
- 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,
asset_upload
- let's setup our app! run
rails new asset_upload -T --database=postgresql
inside your workspace where you want your repo - once we have set up our app, let's
cd
into it, and open withcode .
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!
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
S3
section, let's create our first bucket. Find the bluecreate bucket
button 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 clickcreate 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.
In working with active storage, we need to only work with a couple files
config/storage.yml
config/credentials.yml.enc
active_storage
database tables
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
rails active_storage:install:migrations
rails db:migrate
Now, if you open your schema.rb
file, you should be able to see 3 tables: users
, active_storage_attachments
, and active_storage_blobs
This migration adds two tables - active_storage_blobs
and 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
resources :users
- 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
new.html.erb
andshow.html.erb
files - with all of this, let's fire up our server with rails s and navigate to http://localhost:3000
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.
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 access_key_id
and 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 development.rb
and 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 storage.yml
.
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
gem 'aws-sdk-s3'
Then go back to your terminal and run bundle install
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 new.html.erb
add
<%= 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 show.html.erb
add
<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
heroku create
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>
wheremaster_key
is 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