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 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 🎉
Top comments (0)