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:migrateThis creates two tables:
active_storage_blobs- Stores file metadataactive_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-nameAdd the gem:
ruby
1# Gemfile
2gem 'aws-sdk-s3', require: falseGoogle Cloud Storage
yaml
1google:
2 service: GCS
3 project: your-project
4 credentials: <%= Rails.root.join("path/to/keyfile.json") %>
5 bucket: your-bucket-nameSetting the Service
ruby
1# config/environments/development.rb
2config.active_storage.service = :local
3
4# config/environments/production.rb
5config.active_storage.service = :amazonAttaching Files to Models
has_one_attached
For single file attachments:
ruby
1class User < ApplicationRecord
2 has_one_attached :avatar
3endhas_many_attached
For multiple file attachments:
ruby
1class Article < ApplicationRecord
2 has_many_attached :images
3endUsing 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
21endAttaching 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 %>Download Links
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_atRemoving 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).purgeRemove 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)
6endDirect 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
13endActive Storage makes file handling in Rails simple and flexible!
