DEV Community

Allie Stehney
Allie Stehney

Posted on

How To Create an Image Uploader: Rails Active Storage

Recently, I had to figure out how to create an Image Uploader for a Ruby on Rails application. I had used Paperclip before, so I looked into that, but quickly discovered that Rails 5.2 has Active Storage built into it.

What is ActiveStorage? It's a built-in feature in Ruby on Rails that allows for uploading files to third-party storage (i.e., Amazon S3, Google Cloud Storage, etc.) while saving these images to Active Record objects.

And, as long as you're on at least Rails 5.2 - this is important because this is the the version of Rails that introduced ActiveStorage - the process for getting up and running, for me at least, is and was incredibly smooth.

To start, go into your rails application in the terminal and type:
rails active_storage: install

This command runs a migration that adds two new tables to your database:
active_storage_blobs and active_storage_attachments.

Here's a helpful image I found to describe these tables:
Image showing the CreateActiveStorageTables migration
(image is from this amazing article which you should read if you have a chance).

Make sure to run rails db:migrate to run the migration.

Now that we're set up with the tables in our database, the next step is setting up where we want to store our files that we upload. Go ahead and create this file:
config/storage.yml

In it, we need to specify where we want to store our files that we upload for our development environment, our test environment, and our production environment.

The file should look something like this:

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

amazon:
  service: S3
  access_key_id: ""
  secret_access_key: ""
  bucket: ""
  region: "" # e.g. 'us-east-1'

You don't have to set this up for each environment, however, if you have multiple environments, it is likely that they will each store uploads in a different place so it's good to get this configured. Part of what makes Active Storage so great is that it comes with disk storage for your development and test environments. For production, depending on which service you use, you can configure that to be set up in this file (I chose Amazon S3 above, but have not filled in any of the credentials).

With this in place, we then need to tell Active Storage which service to use and we'll do that on a per-environment basis, too.

In config/environments/development.rb let's add:
config.active_storage.service = :local

which means we'll store files locally for our development environment.

In config/environments/test.rb let's add:
config.active_storage.service = :test

which means we'll temporarily store files locally (notice in the above config/storage.yml file that we're storing to the tmp/storage directory and not the storage directory).

And, lastly, in config/environments/production.rb let's add:
config.active_storage.service = :amazon

which means we'll store our files on Amazon.

With all of this set up, we have two more major steps: Attaching the files to our Model and displaying that attached file to the user in our UI.

To attach the file to our model: use the has_one_attached relationship to set up a one-to-one mapping between the record and the files; or, use the has_many_attached relationship to set up a many-to-one mapping between the record and the files.

has_one_attached example:
A user has one uploaded image associated with it.

class User < ApplicationRecord
  has_one_attached :image
end

has_many_attached example:
A Blog Post has many uploaded images associated with it.

class Post < ApplicationRecord
  has_many_attached :images
end

NOTE: to see how to fully set up the has_many_attached association see the docs. To keep things simple, I'll set up the UI using the has_one_attached relationship.

Finally, to allow users to upload the images in the UI and then display the image back to the user, we can add an upload form field to our page by adding this:

<%= form.file_field :image %>

Add the :image param into your params that you permit in the controller; for example:

def user_params
   params.require(:user).permit(:email, :password, :image)
end

With this in place, we can display the image back to a user (if there is one attached) so that they can see their attached file (in the user#show part of the application):

<% if @user.image.attached? %>
   <img src="<%=(url_for(@user.image)) %>">
<% end %>

attached? is a helpful built-in method that tells us if the image for that user has been attached. You can also call attach on an existing user to attach an image to that user by doing:
user.image.attach(params[:image])

And, there you have it! A way to add an image uploader to your rails application using the built-in Rails Active Storage 🎉

Discussion (0)