RoR File Uploads
ActiveStorage is Rails' built-in system for handling file uploads. It lets users attach images, PDFs, videos, or any file to a model record. ActiveStorage stores the files in a configurable location — local disk for development, cloud storage like Amazon S3 for production — and provides tools to display and process them.
Set Up ActiveStorage
Run the installer to create the required database tables:
rails active_storage:install rails db:migrate
This creates two tables: active_storage_blobs (stores file metadata) and active_storage_attachments (links files to your models).
Attach Files to a Model
Declare the attachment in your model using has_one_attached or has_many_attached:
app/models/user.rb class User < ApplicationRecord has_one_attached :avatar ← one profile picture has_many_attached :documents ← multiple uploaded documents end app/models/product.rb class Product < ApplicationRecord has_one_attached :featured_image has_many_attached :gallery_images end
Add a File Input to Your Form
app/views/users/edit.html.erb
<%= form_with model: @user do |f| %>
<p>
<%= f.label :name %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label :avatar, "Profile Picture" %>
<%= f.file_field :avatar, accept: "image/*" %>
</p>
<%= f.submit "Save Profile" %>
<% end %>
Permit the File in Strong Parameters
private def user_params params.require(:user).permit(:name, :email, :avatar, documents: []) end
:avatar is a single attachment. documents: [] is an array for multiple attachments.
Display Uploaded Files
Show an avatar image: <% if @user.avatar.attached? %> <%= image_tag @user.avatar %> <% else %> <%= image_tag "default_avatar.png" %> <% end %> Show a product's featured image: <% if @product.featured_image.attached? %> <%= image_tag @product.featured_image %> <% end %> Show multiple gallery images: <% @product.gallery_images.each do |image| %> <%= image_tag image %> <% end %> Link to download a document: <% @user.documents.each do |doc| %> <%= link_to doc.filename, rails_blob_path(doc, disposition: "attachment") %> <% end %>
Image Variants — Resize on the Fly
ActiveStorage can generate resized versions of images using the image_processing gem. First, add it to your Gemfile:
gem "image_processing", "~> 1.2"
Then run bundle install. Now you can create variants:
Thumbnail (100x100): <%= image_tag @user.avatar.variant(resize_to_fill: [100, 100]) %> Medium (400x300): <%= image_tag @product.featured_image.variant(resize_to_limit: [400, 300]) %> Convert format: <%= image_tag @photo.image.variant(format: :webp, quality: 80) %>
How ActiveStorage Stores Files
User uploads avatar.jpg
|
v
ActiveStorage creates a blob record:
active_storage_blobs table:
id: 1 | key: "xyz123abc" | filename: "avatar.jpg" | content_type: "image/jpeg"
|
v
ActiveStorage creates an attachment record:
active_storage_attachments table:
id: 1 | name: "avatar" | record_type: "User" | record_id: 5 | blob_id: 1
|
v
File stored at: storage/x/y/z/xyz123abc
(local disk in development)
Configure Storage Services
Storage backends are configured in config/storage.yml:
config/storage.yml
local:
service: Disk
root: <%= Rails.root.join("storage") %>
amazon:
service: S3
access_key_id: <%= ENV["AWS_ACCESS_KEY_ID"] %>
secret_access_key: <%= ENV["AWS_SECRET_ACCESS_KEY"] %>
region: us-east-1
bucket: my-app-bucket
google:
service: GCS
project: my-project
credentials: <%= Rails.root.join("gcs-credentials.json") %>
bucket: my-app-bucket
Switch the active service per environment:
config/environments/development.rb: config.active_storage.service = :local config/environments/production.rb: config.active_storage.service = :amazon
File Validation
Validate file type and size in your model:
class User < ApplicationRecord
has_one_attached :avatar
validate :avatar_requirements
private
def avatar_requirements
return unless avatar.attached?
unless avatar.blob.content_type.start_with?("image/")
errors.add(:avatar, "must be an image file")
end
if avatar.blob.byte_size > 5.megabytes
errors.add(:avatar, "must be smaller than 5MB")
end
end
end
Delete an Attachment
# Remove the avatar @user.avatar.purge # Remove the avatar in the background (faster response) @user.avatar.purge_later
ActiveStorage handles everything from upload to display to cloud storage. It removes the need for external gems like Paperclip or CarrierWave and provides a clean, consistent API for file attachments across your entire application.
