How To Create an Image Uploader: Rails Active Storage
Tags: rails, activestorage, fileuploader
Published: 2019-09-29 23:45:09

---
title: How To Create an Image Uploader: Rails Active Storage
published: true
description: A how-to for creating image upload functionality in a Ruby on Rails app
tags: ruby on rails, rails, Active Storage, file uploader
---

Recently, I had to figure out how to create an Image Uploader for a Ruby on Rails application. I had used [Paperclip]("https://github.com/thoughtbot/paperclip") before, so I looked into that, but quickly discovered that Rails 5.2 has `Active Storage` built into it. 

What is [ActiveStorage]("https://edgeguides.rubyonrails.org/active_storage_overview.html")? It's a built-in feature in Ruby on Rails that allows for uploading files to third-party storage (i.e., [Amazon S3]("https://aws.amazon.com/free/storage/"), [Google Cloud Storage]("https://cloud.google.com/storage/"), etc.) while saving these images to Active Record objects.

And, as long as you're on at least [Rails 5.2]("https://guides.rubyonrails.org/5_2_release_notes.html") - 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](https://thepracticaldev.s3.amazonaws.com/i/7to4xm7jcw1s5tnd0h7q.png)
(image is from [this amazing article]("https://medium.com/@anaharris/how-to-add-image-upload-functionality-to-your-rails-app-9f7fc3f3d042") 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]("https://edgeguides.rubyonrails.org/active_storage_overview.html#has-many-attached"). 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 :tada: