Como implementar upload de arquivos com Active Storage no Rails | has_one_attached・Compatibilidade com S3
Active Storage, incluído por padrão no Rails 5.2, é um framework para upload de arquivos. É o mecanismo oficial que substitui as soluções legadas <code>CarrierWave</code> e <code>Paperclip</code>, permitindo alternar perfeitamente entre armazenamento em disco local, AWS S3, Google Cloud Storage e Microsoft Azure. Este artigo explica sistematicamente desde a configuração inicial do Active Storage, a definição de <code>has_one_attached</code>, validações, configuração do S3 (config/storage.yml) até redimensionamento de imagens (Variant).
Configuração do Active Storage
Para começar a usar o Active Storage, gere a migração com <code>rails active_storage:install</code> e aplique-a com <code>rails db:migrate</code>. Isso cria três tabelas: <code>active_storage_blobs</code>, <code>active_storage_attachments</code> e <code>active_storage_variant_records</code>.
# Active Storage のマイグレーションを生成
rails active_storage:install
# マイグレーションを実行
rails db:migrate
# 生成されるテーブル:
# active_storage_blobs — ファイルのメタデータ(ファイル名、サイズ、MIMEタイプなど)
# active_storage_attachments — モデルとblobの紐付け(ポリモーフィック関連)
# active_storage_variant_records — 変換済み画像のキャッシュ
# Gemfile
gem 'image_processing', '>= 1.2' # Variant(画像リサイズ)に必要
# gem 'mini_magick' # ImageMagick を使う場合
# gem 'ruby-vips' # libvips を使う場合(高速)
bundle install
# ImageMagick のインストール(Ubuntu/Debian)
sudo apt-get install imagemagick
# macOS(Homebrew)
brew install imagemagick vips
Definição de has_one_attached e has_many_attached
<code>has_one_attached</code> define anexo de arquivo 1-para-1, e <code>has_many_attached</code> define 1-para-muitos. Ao adicionar ao modelo, é possível anexar, obter e deletar arquivos.
# app/models/user.rb
class User < ApplicationRecord
# 1つのアバター画像
has_one_attached :avatar
# 複数の添付ファイル
has_many_attached :documents
end
# app/models/article.rb
class Article < ApplicationRecord
has_one_attached :cover_image
has_many_attached :attachments
# バリデーション(active_storage_validations gem を使用)
validates :cover_image,
content_type: { in: %w[image/jpeg image/png image/webp], message: 'はJPEG・PNG・WebPのみ有効です' },
size: { less_than: 5.megabytes, message: 'は5MB以下にしてください' }
validates :attachments,
content_type: %w[application/pdf image/jpeg image/png],
size: { less_than: 20.megabytes }
end
# コントローラーでの操作例
# app/controllers/users_controller.rb
class UsersController < ApplicationController
def update
@user = current_user
if @user.update(user_params)
redirect_to @user, notice: 'プロフィールを更新しました。'
else
render :edit, status: :unprocessable_entity
end
end
private
def user_params
params.require(:user).permit(:name, :avatar, documents: [])
end
end
<!-- app/views/users/edit.html.erb -->
<%= form_with model: @user do |f| %>
<div>
<%= f.label :avatar, 'プロフィール画像' %>
<!-- enctype は form_with が自動設定 -->
<%= f.file_field :avatar, accept: 'image/jpeg,image/png,image/webp' %>
<%# 現在の画像を表示 %>
<% if @user.avatar.attached? %>
<%= image_tag @user.avatar, width: 100 %>
<% end %>
</div>
<div>
<%= f.label :documents, '添付ファイル(複数可)' %>
<%= f.file_field :documents, multiple: true %>
</div>
<%= f.submit '保存' %>
<% end %>
Validação com a gem active_storage_validations
Como o Active Storage padrão tem validação de modelo fraca, você pode usar a gem <code>active_storage_validations</code> para validar declarativamente Content-Type, tamanho, número de pixels em altura e largura, entre outros.
# Gemfile
gem 'active_storage_validations'
# app/models/product.rb
class Product < ApplicationRecord
has_one_attached :main_image
has_many_attached :gallery_images
# content_type バリデーション
validates :main_image,
attached: true,
content_type: {
in: %w[image/jpeg image/png image/webp image/gif],
message: 'は画像ファイル(JPEG/PNG/WebP/GIF)を選択してください'
},
size: {
between: 1.kilobyte..10.megabytes,
message: 'は1KB〜10MBの範囲で指定してください'
},
dimension: {
width: { min: 200, max: 4096 },
height: { min: 200, max: 4096 },
message: '縦横ともに200〜4096pxの範囲で指定してください'
}
validates :gallery_images,
content_type: %w[image/jpeg image/png image/webp],
size: { less_than: 5.megabytes },
limit: { max: 10, message: '画像は最大10枚まで添付できます' }
end
Configuração de config/storage.yml
Os serviços de armazenamento do Active Storage são definidos em <code>config/storage.yml</code> e você alterna qual serviço usar nas configurações específicas do ambiente em <code>config/environments/</code>.
# config/storage.yml
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
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: <%= ENV['AWS_REGION'] %>
bucket: <%= ENV['AWS_BUCKET'] %>
# CloudFront を使う場合
# upload: { cache_control: 'max-age=3600' }
google:
service: GCS
project: <%= ENV['GOOGLE_CLOUD_PROJECT'] %>
credentials: <%= ENV['GOOGLE_CLOUD_CREDENTIALS'] %>
bucket: <%= ENV['GOOGLE_CLOUD_BUCKET'] %>
azure:
service: AzureStorage
storage_account_name: <%= ENV['AZURE_STORAGE_ACCOUNT_NAME'] %>
storage_access_key: <%= ENV['AZURE_STORAGE_ACCESS_KEY'] %>
container: <%= ENV['AZURE_STORAGE_CONTAINER'] %>
# config/environments/production.rb
Rails.application.configure do
# S3 を使用
config.active_storage.service = :amazon
end
# config/environments/development.rb
Rails.application.configure do
# ローカルディスクを使用
config.active_storage.service = :local
end
# Gemfile(S3を使う場合)
gem 'aws-sdk-s3', require: false
# Google Cloud Storage を使う場合
gem 'google-cloud-storage', '~> 1.11', require: false
# Azure を使う場合
gem 'azure-storage-blob', require: false
Redimensionamento de imagem com Variant
Ao usar a função Variant do Active Storage, você pode redimensionar e converter dinamicamente imagens salvas para exibição. Os resultados de conversão são armazenados em cache, para que sejam entregues rapidamente a partir da segunda vez.
# ビューでの Variant 使用例
# app/views/users/show.html.erb
<%# サムネイル表示(100x100にリサイズ)%>
<%= image_tag @user.avatar.variant(resize_to_limit: [100, 100]) %>
<%# アスペクト比を保ちながら幅300pxに収める %>
<%= image_tag @user.avatar.variant(resize_to_limit: [300, nil]) %>
<%# 中央でトリミングして正方形に(400x400)%>
<%= image_tag @user.avatar.variant(resize_to_fill: [400, 400]) %>
<%# WebP に変換(高画質・小容量)%>
<%= image_tag @user.avatar.variant(convert: 'webp', resize_to_limit: [800, 600]) %>
<%# 画質を下げてファイルサイズを削減 %>
<%= image_tag @user.avatar.variant(
resize_to_limit: [1200, 900],
saver: { quality: 80 }
) %>
# モデルでよく使う Variant を定義する(ヘルパーメソッド)
# app/models/user.rb
class User < ApplicationRecord
has_one_attached :avatar
def avatar_thumbnail
avatar.variant(resize_to_fill: [80, 80], convert: 'webp')
end
def avatar_medium
avatar.variant(resize_to_limit: [400, 400], convert: 'webp', saver: { quality: 85 })
end
end
# ビューで使用
# <%= image_tag @user.avatar_thumbnail if @user.avatar.attached? %>
Arquivo de teste disponível para usar neste artigo (gratuito)
- → <a href="/ja/files/images/png/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">Imagem PNG de teste (1MB)</a> — Para teste de <code>has_one_attached</code> e <code>Variant</code>
- → <a href="/ja/files/pdf/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">Arquivo de teste PDF (1MB)</a> — Para verificar has_many_attached e upload S3
- → <a href="/ja/files/zip/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">Arquivo de teste ZIP (1MB)</a> — Para verificação de validação de content_type
Artigos relacionados
- → <a href="/ja/blog/s3-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">Resumo dos limites de upload de arquivos do AWS S3 e CloudFront</a>
- → <a href="/ja/blog/laravel-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Guia de implementação de upload de arquivos no Laravel | Validação, Storage e suporte S3</a>
- → <a href="/ja/blog/file-validation-checklist/" class="text-primary-600 dark:text-primary-400 hover:underline">Lista de verificação de implementação de validação de arquivo para formulários web</a>