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.

Leave a Comment

Your email address will not be published. Required fields are marked *