はじめに
この記事では Rails 5.2 の目玉機能である Active Storage を試してみます。具体的にはベータ版の Rails 5.2 を使って、添付ファイルを AWS S3 にアップロードしてみます。
なお JavaScript を使って直接ファイルを Web クライアントから直接アップロードできる Direct uploads という機能については触れませんので悪しからず。
準備
Rails のバージョン 5.2.0.beta2
を使って rails new
します。
gem 'rails', '5.2.0.beta2'
追加で次の Gem をインストールします。
gem 'mini_magick', '4.8.0'
gem 'aws-sdk', '3.0.1'
次のコマンドを実行します。
$ bin/rails active_storage:install
マイグレーションを実行すると active_storage_blobs
(メタデータ用) と active_storage_attachments
(ポリモーフィック関連用) という 2 つのテーブルが作成されます。
$ bin/rails db:migrate
AWS アクセスキーを設定します。
$ EDITOR=vim bin/rails credentials:edit
aws:
access_key_id: REPLACE_WITH_ACCESS_KEY_ID
secret_access_key: REPLACE_WITH_SECRET_ACCESS_KEY
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: # 略
その後 config/storage.yml
で amazon
の設定を入力します。
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: ap-northeast-1
bucket: hidamari
最後に Active Storage が AWS S3 を使うように設定を更新します。
config.active_storage.service = :amazon # development 環境の初期値は :local になっている。
動作検証
次の User モデルを使って動作を検証します。なおデータベースには PostgreSQL を使いました。
モデル : 添付ファイル = 1 : 1 の場合
class User < ApplicationRecord
has_one_attached :avatar
end
まだ画像が添付されていない状態です。
yuno = User.find_by(name: 'ゆの')
yuno.avatar.attached? #=> false
ではファイルをアップロードしてみます。
filepath = File.join(Dir.home, 'Downloads/yuno.jpg')
yuno.avatar.attach(io: File.open(filepath), filename: File.basename(filepath), content_type: 'image/jpg')
# Enqueued ActiveStorage::AnalyzeJob (Job ID: ca6acf25-2971-4fea-a4a7-7c1103b5ebf5) to Async(default) with arguments: #<GlobalID:0x00007fc30dc37bc0 @uri=#<URI::GID gid://activestorage-sandbox/ActiveStorage::Blob/1>>
#=> #<ActiveStorage::Attachment id: 1, name: "avatar", record_type: "User", record_id: 1, blob_id: 1, created_at: "2017-12-04 06:03:44">
attach
メソッドを実行すると ActiveStorage::AnalyzeJob
というバックグラウンドジョブが登録されます。登録後してすぐにジョブが実行されました。
Performing ActiveStorage::PurgeJob (Job ID: ca6acf25-2971-4fea-a4a7-7c1103b5ebf5) from Async(default) with arguments: #<GlobalID:0x00007fc30dc37bc0 @uri=#<URI::GID gid://activestorage-sandbox/ActiveStorage::Blob/1>>
# 略
Performed ActiveStorage::PurgeJob (Job ID: ca6acf25-2971-4fea-a4a7-7c1103b5ebf5) from Async(default) in 152.65ms
# 略
Performed ActiveStorage::AnalyzeJob (Job ID: 36223552-3459-4325-943a-28de6d1d45f0) from Async(default) in 322.1ms
アップロード後に url_for を使うと期限付き URL (デフォルトでは 5 分) を発行できます。
yuno.avatar.attached? #=> true
# Rails console で url_for を使う。
# 設定ファイルで default_url_options を設定済であれば app.url_for でも動くかもしれない。
include(Rails.application.routes.url_helpers)
default_url_options[:host] = 'localhost:3000'
url_for(yuno.avatar)
#=> "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBEUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--726ebe29a44d2ee4214513f49e4a9c55b4af76f5/yuno.jpg"
Rails server を起動してこの URL にアクセスすると、S3 上の画像ファイルをダウンロードするための期限付き URL にリダイレクトされます。
モデル : 添付ファイル = 1 : 多 の場合
class User < ApplicationRecord
has_many_attached :files
end
ひとつのモデルに複数のファイルを添付することも可能です。
yuno = User.find_by(name: 'ゆの')
yuno.files.attached? #=> false
filepath = File.join(Dir.home, 'Downloads/yuno.pdf')
yuno.files.attach(io: File.open(filepath), filename: File.basename(filepath), content_type: 'application/pdf')
filepath = File.join(Dir.home, 'Downloads/yuno.mp4')
yuno.files.attach(io: File.open(filepath), filename: File.basename(filepath), content_type: 'video/mp4')
yuno.files.attached? #=> true
yuno.files.count #=> 2
ちなみに動画ファイルや PDF ファイルの場合、プレビュー画像 (サムネイル用?) を生成することも可能なようです。README.md には説明がまだ書かれていませんが、単体テストを読んでいて知りました。
video_file = yuno.files.last
preview = video_file.blob.preview(resize: '640x280').processed # 実行時に ffmpeg のログが出力された。
url_for(preview.image)
#=> "http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBFUT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--262878ebb566bf1aa31ad7a8355147a25422cd37/yuno.png"
感想
従来は添付ファイルのアップロードに Paperclip を使っていましたが、同様のことを外部の Gem なしに実現できるのはすごく楽ですね。正式版が待ち遠しいです