Skip
Arish's avatar

43. Active Storage Setup


Active Storage

Active Storage is Rails' built-in solution for uploading files to cloud storage services like Amazon S3, Google Cloud Storage, or Microsoft Azure Storage. It can also store files locally.

Installation

Active Storage comes with Rails. Install the migrations:

bash
1rails active_storage:install
2rails db:migrate

This creates two tables:

  • active_storage_blobs - Stores file metadata
  • active_storage_attachments - Polymorphic join table linking blobs to models

Configuration

Local Storage (Development)

yaml
1# config/storage.yml
2local:
3  service: Disk
4  root: <%= Rails.root.join("storage") %>

Amazon S3

yaml
1# config/storage.yml
2amazon:
3  service: S3
4  access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
5  secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
6  region: us-east-1
7  bucket: your-bucket-name

Add the gem:

ruby
1# Gemfile
2gem 'aws-sdk-s3', require: false

Google Cloud Storage

yaml
1google:
2  service: GCS
3  project: your-project
4  credentials: <%= Rails.root.join("path/to/keyfile.json") %>
5  bucket: your-bucket-name

Setting the Service

ruby
1# config/environments/development.rb
2config.active_storage.service = :local
3
4# config/environments/production.rb
5config.active_storage.service = :amazon

Attaching Files to Models

has_one_attached

For single file attachments:

ruby
1class User < ApplicationRecord
2  has_one_attached :avatar
3end

has_many_attached

For multiple file attachments:

ruby
1class Article < ApplicationRecord
2  has_many_attached :images
3end

Using Attachments

In Forms

erb
1<%= form_with model: @user do |f| %>
2  <%= f.label :avatar %>
3  <%= f.file_field :avatar %>
4  <%= f.submit %>
5<% end %>
6
7<!-- Multiple files -->
8<%= form_with model: @article do |f| %>
9  <%= f.label :images %>
10  <%= f.file_field :images, multiple: true %>
11  <%= f.submit %>
12<% end %>

In Controllers

ruby
1class UsersController < ApplicationController
2  def create
3    @user = User.new(user_params)
4    if @user.save
5      redirect_to @user
6    else
7      render :new
8    end
9  end
10  
11  def update
12    @user = User.find(params[:id])
13    @user.update(user_params)
14  end
15  
16  private
17  
18  def user_params
19    params.require(:user).permit(:name, :email, :avatar, images: [])
20  end
21end

Attaching Files Programmatically

ruby
1# Attach from file path
2user.avatar.attach(io: File.open('/path/to/file.jpg'), filename: 'avatar.jpg')
3
4# Attach from URL
5require 'open-uri'
6user.avatar.attach(io: URI.open('https://example.com/image.jpg'), filename: 'avatar.jpg')
7
8# Attach from uploaded file
9user.avatar.attach(params[:avatar])
10
11# Multiple files
12article.images.attach(params[:images])

Displaying Attachments

Images

erb
1<% if @user.avatar.attached? %>
2  <%= image_tag @user.avatar %>
3  
4  <!-- With size -->
5  <%= image_tag @user.avatar, size: "100x100" %>
6  
7  <!-- Get URL -->
8  <%= url_for(@user.avatar) %>
9  
10  <!-- Rails URL helper -->
11  <%= rails_blob_path(@user.avatar, only_path: true) %>
12<% end %>
erb
1<%= link_to "Download", rails_blob_path(@document.file, disposition: "attachment") %>
2
3<!-- Or inline -->
4<%= link_to "View", rails_blob_path(@document.file, disposition: "inline") %>

Multiple Attachments

erb
1<% @article.images.each do |image| %>
2  <%= image_tag image, class: "gallery-image" %>
3<% end %>

Checking Attachments

ruby
1# Check if attached
2user.avatar.attached?
3
4# Check how many
5article.images.count
6
7# Get blob metadata
8user.avatar.blob.filename
9user.avatar.blob.content_type
10user.avatar.blob.byte_size
11user.avatar.blob.created_at

Removing Attachments

ruby
1# Remove single attachment
2user.avatar.purge       # Synchronously
3user.avatar.purge_later # Background job
4
5# Remove from collection
6article.images.find(blob_id).purge

Remove in Form

erb
1<%= form_with model: @user do |f| %>
2  <% if @user.avatar.attached? %>
3    <%= image_tag @user.avatar, size: "100x100" %>
4    <%= f.check_box :remove_avatar, {}, true, false %>
5    <%= f.label :remove_avatar, "Remove avatar" %>
6  <% end %>
7  
8  <%= f.file_field :avatar %>
9  <%= f.submit %>
10<% end %>
ruby
1# Controller
2def update
3  @user = User.find(params[:id])
4  @user.avatar.purge if params[:user][:remove_avatar] == "true"
5  @user.update(user_params)
6end

Direct Uploads

Enable JavaScript direct uploads for better UX:

javascript
1// app/javascript/application.js
2import * as ActiveStorage from "@rails/activestorage"
3ActiveStorage.start()
erb
1<%= form_with model: @article, local: true do |f| %>
2  <%= f.file_field :images, multiple: true, direct_upload: true %>
3  <%= f.submit %>
4<% end %>

Callbacks

ruby
1class User < ApplicationRecord
2  has_one_attached :avatar
3  
4  after_commit :process_avatar, on: [:create, :update]
5  
6  private
7  
8  def process_avatar
9    if avatar.attached?
10      # Do something after avatar is uploaded
11    end
12  end
13end

Active Storage makes file handling in Rails simple and flexible!