How to add image upload functionality to your Rails app

Ana
4 min readNov 29, 2018

--

…using Active Storage! 💫

Active Storage is a gem included in Rails 5.2 that allows you to upload, store and display files. It comes with an option to generate image representations of both image and non-image uploads (such as PDF files and videos). In this short step-by-step guide I will show you how to add a simple image upload to your app, as well as how to show the uploaded image on your page.

Setup

After creating your application, add Active Storage by running the following command in your terminal. Make sure you’re using Rails 5.2!

rails active_storage:install

This will generate a migration that adds two tables to your database:

class CreateActiceStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false

t.index [ :key ], unique: true
end

create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false

t.datetime :created_at, null: false

t.index [ :record_type, :record_id, :name, :blob_id ],
name: "index_active_storage_attachments_uniqueness", unique: true
end
end
end

You don’t have to create an Image model or an Image table. Active Storage will take care of everything for you. Just make sure to run rails db:migrate before you continue.

Next step is to declare your storage services in ‘config/storage.yml’. If you’re planning to use Amazon S3, Google Cloud or Microsoft Azure Storage, check out the official docs for more info. If you’re building development or test app, or you just want to practice using Ruby and Rails, built-in disk storage is probably enough. To use disk storage, your storage.yml file should have this code:

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

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

Next, check your ‘config/environments/development.rb’ file, it should look like this:

config.active_storage.service = :local

In order to use tests, make sure your ‘config/environments/test.rb’ has this code:

config.active_storage_service = :test

Great! Next thing we need to do is attach files to our models. I’m building an app that allows users to upload profile photo. I’m using the has_one_attached relationship that allows every User object to have only one image attached to it. My ‘app/models/user.rb’ file looks like this (*you can call your image file anything you like):

class User < ApplicationRecord
has_one_attached :avatar

In my user#new and user#edit files I’ve added a picture field to my form:

<%= f.label "Profile Photo" %>
<%= f.file_field :avatar %><br>

that will in the browser (with some simple CSS styling) look like this:

You don’t need to add any special code to your controller, just make sure you include :avatar in your strong params.

If you want to add image from params to an already existing object (e.g. via an edit form), use the following syntax in your controller:

@user.avatar.attach(params[:avatar])

or if you’re uploading it from a folder:

@user.avatar.attach(io: File.open('app/assets/images/palceholder.png'), filename: 'placeholder.png', content_type: 'image/png')

Displaying image

Now that we know how to upload an image, let’s learn how to display it in the browser. Let’s say I want to display it on a user’s profile (show) page. In user#show I’ve added the following code:

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

Let’s break this down:
.avatar.attached? is a handy built-in method we can call to check if an image(avatar) for that user has been attached. If such file exists, url_for(@user.avatar) will create a URL to it. We can then pass that URL to the HTML image tag source that will display it in the browser.

And voilà! You just made your Rails app way more cool by adding a few simple lines of code! 😎

Notes and tips:

1. Keep in mind that when you declare has_one_attached relationship one and only one image can be attached to an object. In order to change it, you’ll have to delete it first (syntax: @user.avatar.purge).
2. You can upload more than one image/file per object. Just use has_many_attached macro instead.
3. When you want to upload an image from a folder, make sure you use the full path. Active Storage docs never mention this, but in my experience your upload will work only if you provide the full absolute path (like shown in the code snippet above). You don’t have to declare a content_type but it is considered to be good practice.
4. Calling .avatar will return an object even when file is not uploaded. Make sure you use .avatar.attached? to check if the image you’re looking for exists.
5. Official docs are very well written so make sure you use them!

--

--

Ana

I moved from Europe to USA and from human languages to programming languages.