Rails 6.1 adds support for multiple storage services to Active Storage


Rails 5.2 added a built-in solution for handling file uploads using Active Storage.

Let’s say, we have an Candidate model that has two attachments resume and cover_letter.

class Candidate < ApplicationRecord
  has_one_attached :resume
  has_one_attached :cover_letter
end

# Attach cover_letter to the candidate.
candidate = Candidate.last
candidate.cover_letter.attach(
  io: File.open("path/to/cover_letter"),
  filename: "cover_letter.txt",
  content_type: "text/plain"
)

Before Rails 6.1, only one cloud storage service (Amazon S3, Google cloud storage, Microsoft Azure storage) could be used for overall application.

But, there could be cases where our application might require to upload images to different storage services.

In Rails 6.1

For the above example, let’s say we want to upload resume to Amazon S3 and cover_letter to Google cloud.

We need to pass service parameter to has_one_attached method as shown below:

class Candidate < ApplicationRecord
  has_one_attached :resume, service: :amazon
  has_one_attached :cover_letter, service: :google
end

In the above example, resume of the candidate is stored to amazon and cover_letter to google.

storage.yml can be configured as below for multiple storage services:

test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

amazon:
  service: s3
  access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
  secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
  region: <%= Rails.application.credentials.dig(:aws, :aws_region) %>
  bucket: <bucket_name>

google:
  service: google_cloud
  project: <project_name>
  credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
  bucket: <bucket_name>

NOTE:

  • If the service is not specified the default service set in config/environments is used for storage.

  • If specified service is not properly configured, has_one_attached would throw an error.

  • If our project is already using Active Storage and when we upgrade to Rails 6.1, we should run rake app:update to make sure service_name column is added to the internal ActiveStorageBlob model.