Implementierung von Datei-Uploads mit Active Storage in Rails | has_one_attached・S3-Unterstützung
Active Storage, das seit Rails 5.2 standardmäßig enthalten ist, ist ein Framework für Datei-Uploads. Es ist der offizielle Mechanismus, der die älteren Lösungen <code>CarrierWave</code> und <code>Paperclip</code> ersetzt und ermöglicht einen nahtlosen Wechsel zwischen lokalem Speicher, AWS S3, Google Cloud Storage und Microsoft Azure. Dieser Artikel erläutert systematisch die Einrichtung von Active Storage, die Definition von <code>has_one_attached</code>, Validierungen, S3-Konfiguration (config/storage.yml) und Bildgrößenänderung (Variant).
Setup von Active Storage
Um mit Active Storage zu beginnen, generieren Sie die Migration mit <code>rails active_storage:install</code> und wenden Sie sie mit <code>rails db:migrate</code> an. Dies erstellt drei Tabellen: <code>active_storage_blobs</code>, <code>active_storage_attachments</code> und <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
Definition von has_one_attached und has_many_attached
<code>has_one_attached</code> definiert 1-zu-1-Dateianlagen, <code>has_many_attached</code> definiert 1-zu-viele. Durch Hinzufügen zum Modell können Dateien angehängt, abgerufen und gelöscht werden.
# 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 %>
Validierung mit dem active_storage_validations Gem
Da die Standard-Validierung von Active Storage-Modellen schwach ist, können Sie das Gem <code>active_storage_validations</code> verwenden, um Content-Type, Größe, Pixelhöhe und -breite etc. deklarativ zu validieren.
# 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
Konfiguration von config/storage.yml
Die Storage-Services von Active Storage werden in <code>config/storage.yml</code> definiert und Sie wechseln in den umgebungsspezifischen Konfigurationen in <code>config/environments/</code>, welcher Service verwendet werden soll.
# 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
Bildgrößenänderung mit Variant
Mit der Variant-Funktion von Active Storage können Sie gespeicherte Bilder dynamisch in der Größe ändern und konvertieren, um sie anzuzeigen. Die Konversionsergebnisse werden zwischengespeichert, daher wird die Bereitstellung ab dem zweiten Mal schnell durchgeführt.
# ビューでの 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? %>
Testdatei zur Verwendung in diesem Artikel (kostenlos)
- → <a href="/ja/files/images/png/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PNG-Testbild (1MB)</a> — Zum Test von <code>has_one_attached</code> und <code>Variant</code>
- → <a href="/ja/files/pdf/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">PDF-Testdatei (1MB)</a> — Zur Überprüfung von has_many_attached und S3-Upload
- → <a href="/ja/files/zip/1mb/" class="text-primary-600 dark:text-primary-400 hover:underline">ZIP-Testdatei (1MB)</a> — Zur Überprüfung der content_type-Validierung
Verwandte Artikel
- → <a href="/ja/blog/s3-upload-limit/" class="text-primary-600 dark:text-primary-400 hover:underline">Zusammenfassung der Datei-Upload-Limits von AWS S3 und CloudFront</a>
- → <a href="/ja/blog/laravel-file-upload/" class="text-primary-600 dark:text-primary-400 hover:underline">Implementierungsleitfaden für Datei-Uploads in Laravel | Validierung, Storage und S3-Unterstützung</a>
- → <a href="/ja/blog/file-validation-checklist/" class="text-primary-600 dark:text-primary-400 hover:underline">Checkliste zur Implementierung der Dateiverifizierung für Web-Formulare</a>