Cómo implementar carga de archivos con Rails Active Storage | Soporte has_one_attached y S3
Active Storage, incluido por defecto en Rails 5.2, es un marco de trabajo para cargas de archivos. Es la solución oficial que reemplaza CarrierWave y Paperclip, permitiendo cambiar sin problemas entre almacenamiento en disco local, AWS S3, Google Cloud Storage y Microsoft Azure. Este artículo cubre sistemáticamente la configuración de Active Storage, la definición de has_one_attached, validación, configuración de S3 (config/storage.yml) y cambio de tamaño de imagen (Variant).
Configuración de Active Storage
Para comenzar a usar Active Storage, genere migraciones con rails active_storage:install y aplíquelas con rails db:migrate. Esto crea tres tablas: active_storage_blobs, active_storage_attachments y active_storage_variant_records.
# 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
Definición de has_one_attached y has_many_attached
has_one_attached define archivos adjuntos de uno a uno, y has_many_attached define archivos adjuntos de uno a muchos. Solo agrégalos al modelo para adjuntar, recuperar y eliminar archivos.
# 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 %>
Validación con la gema active_storage_validations
Como la validación estándar del modelo de Active Storage es débil, el uso de la gema active_storage_validations permite validar de forma declarativa Content-Type, tamaño, ancho y alto en píxeles, entre otros.
# 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
Configuración de config/storage.yml
Los servicios de almacenamiento de Active Storage se definen en config/storage.yml, y cambia qué servicio usar mediante la configuración específica del entorno en config/environments/.
# 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
Cambio de tamaño de imagen con Variant
Usando la función Variant de Active Storage, puede cambiar el tamaño y transformar dinámicamente imágenes guardadas para mostrarlas. Los resultados de la conversión se almacenan en caché, por lo que se entregan rápidamente a partir de la 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? %>
❓ Preguntas frecuentes
¿Cuál es la diferencia entre Active Storage y CarrierWave?
¿Cómo elimino archivos con Active Storage?
¿Cómo accedo a archivos privados de S3 con URLs firmadas en Active Storage?
Archivos de prueba para este artículo (gratis)
- → Imagen de prueba PNG (1MB) — para probar
has_one_attachedyVariant - → Archivo de prueba PDF (1MB) — Para verificar has_many_attached y carga a S3
- → Archivo de prueba ZIP (1MB) — para validar la validación de content_type