Upload Photo with Active Storage
Build Blog with Ruby on Rails (Part 4)
In this part, I'm going to share with you how to upload a cover photo for Post using Active Storage component in Ruby on Rails application.
What'll we learn:
- Introduction to Active Storage
- Setup Active Storage
- Configurate Amazon S3 service to store files
- Using Active Storage to upload photos
- Show the cover photo of Post in Homepage
- Solution to N + 1 queries
- Conclusion
- Part 1: Getting Started with Ruby on Rails
- Part 2: CRUD Post for Blog application
- Part 3: Using Bootstrap for Blog application
Introduce Active Storage
Active Storage is a great feature in Rails 5.2. It’s designed to upload files to a cloud storage service like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage and attaching those files to Active Record objects.
Active Storage comes with a local disk-based service for development and testing.
Using Active Storage, Rails application can transform image uploads with ImageMagick, generate image representations of non-image uploads like PDFs and videos, and extract metadata from arbitrary files.
Setup Active Storage
Run command:
rails active_storage:install
It’ll create a migration file like this: db/migrate/YYYYYYYYY_create_active_storage_tables.active_storage.rb
class CreateActiveStorageTables < 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 t.foreign_key :active_storage_blobs, column: :blob_id end end end
The migration will create 2 tables, which uses to store files.
- active_storage_blobs
- active_storage_attachments
And then, execute the migration:
rails db:migrate == 20200404135729 CreateActiveStorageTables: migrating ======= -- create_table(:active_storage_blobs) -> 0.0055s -- create_table(:active_storage_attachments) -> 0.0030s == 20200404135729 CreateActiveStorageTables: migrated (0.0089s)
Config Services where store files:
Active Storage declares services in config/storage.yml. Open this file, you can see the default config like below:
test: service: Disk root: <%= Rails.root.join("tmp/storage") %> local: service: Disk root: <%= Rails.root.join("storage") %>
This configuration that means our application declare two services named test and local. Both of these services use Disk service.
Config Amazon S3 in Active Storage
We using Amazon S3 to store photos, so have to add a new service with named amazon to config/storage.yml :
amazon: service: S3 access_key_id: your_access_key_id secret_access_key: your_secret_access_key region: your_region bucket: your_own_bucket
Add the aws-sdk-s3 gem to your Gemfile:
gem "aws-sdk-s3", require: false
Each environment often uses a different service.
In the development environment, we can use the Disk service by adding the config to config/environments/development.rb:
# Store uploaded files on the local file system config.active_storage.service = :local
In the production environment, we use the Amazon S3 service by adding the following code to config/environments/production.rb:
# Store uploaded files on Amazon S3 config.active_storage.service = :amazon
Don’t forget to restart the server when updating the configuration in each environment.
____________________
Using Active Storage to upload Photo for Post
Each post has one cover_photo attached to it.
We use has_one_attached macro to sets up a one-to-one relationship between Post and Photo.
We use has_one_attached macro to sets up a one-to-one relationship between Post and Photo.
class Post < ApplicationRecord has_one_attached :cover_photo end
The has_one_attached macro sets up a one-to-one mapping between records and files.
In Form:
We use file_field helper method to upload photo.
# app/views/posts/_form.html.erb <div class="form-group"> <%= form.label :cover_photo %> <%= form.file_field :cover_photo, class: 'form-control' %> </div>
In Post Controller
Update post_params to create Post with cover_photo
class PostsController < ApplicationController [...] private def post_params params.require(:post).permit(:title, :content, :cover_photo) end end
Show the cover Photo after uploading:
# app/views/posts/show.html.erb <p> <strong>Cover Photo:</strong> <%= image_tag @post.cover_photo %> </p>
We will receive a small bug when view the Post which has not added the cover photo yet.
The errors: cover photo attachment of post is nil
Can't resolve image into URL: to_model delegated to attachment, but attachment is nil
To avoid this error, we should check attachment before showing the cover photo:
<% if @post.cover_photo.attached? %> <%= image_tag @post.cover_photo, width: '100%' %> <% end %>
Call cover_photo.attached? to determine whether a post has a cover photo.
Transforming Images
Transforming images is a process that supports to create variant photos from an uploaded photo.
To enable this functionality, we add the image_processing gem to your Gemfile:
gem 'image_processing', '~> 1.2'
Run bundle to install:
bundle install
Don’t forget to restart the server!
Now, we can create a new variation of the photo, call variant method on the Blob.
You can pass any transformation to the method supported by the processor (default is MiniMagick).
We'll create a variation of Cover Photo by resize original photo to "300x200" :
post.cover_photo.variant(resize: "300x200")
Show the Variation of cover Photo in Homepage:
# app/views/home/index.html.erb <% if post.cover_photo.attached? %> <%= image_tag post.cover_photo.variant(resize: "300x200")%> <% end %>
Eager loading Active Storage to avoids N+1 queries: Include the attached blobs in the query
Post.with_attached_cover_photo
View the result:

You can change the transformation by each variant, Active Storage will transform the original image into the specified format (transformation).
Type of transformation you can view more in image_processing gem.
____________________
Type of transformation you can view more in image_processing gem.
____________________
Conclusion
In this article, I guided you step by step use Active Storage component to upload photos in Rails application.
In part 5, I'm going to share with you about Action Text, which helps us add a rich-text-editor for writing Post in Blog application.
References:
In part 5, I'm going to share with you about Action Text, which helps us add a rich-text-editor for writing Post in Blog application.
References: