Active Storageについて調べると、「軽く触ってみた」ノリの記事ばかりで、特に複数のファイルを扱う方法などがなかなか見つからなかったので、使い方のまとめ記事を作りました。
Active Storage とは
Active Storageは、Rails5.2で追加された、ファイルアップロードを行うための機能です。これを使えば、フォームで画像の投稿機能などが簡単に作れます。また、Amazon S3, Google Cloud Storage, Microsoft Azure Storageなどのクラウドストレージサービスに対するファイルのアップロードを簡単に行うことができます。クラウドストレージの他に、ローカルディスクにファイルを保存することもできます。
Paperclip, Carrierwave, Dragonflyなどのgemの代わりになるとされていますが、現時点ではバリデーションとキャッシュ関連の機能は残念ながら備えていません。使うにはRails 5.2以上にアップグレードする必要があります。
Active Storage を使えるようにする
$ rails active_storage:install
$ rails db:migrate
ここは気になる方だけ読んでいただければ大丈夫なのですが、このマイグレーションによってactive_storage_blobs
とactive_storage_attachments
という名前のテーブルが2つ生成されます。これらのテーブルはBlob
とAttachment
の2つのモデルが使います。Blob
はファイル名、ファイルの種類、バイト数、誤り検出符号などのメタデータを保持するモデルで、Attachment
モデルは、BlobオブジェクトとActive Recordオブジェクトを紐付けるための中間テーブルです。なお、Active Storageを使う際、直接Blob
とAttachment
モデルに触れる必要はありません。(時間返せ)
Active Record モデルを用意する
コメントに1枚、もしくは、複数枚の画像を添付できるようにしたいとしましょう。まずはComment
モデルを用意する必要があります。generate resource
コマンドで基本的なルートと、モデル、空のコントローラを生成しましょう。
$ rails g resource comment content:text
$ rails db:migrate
ここで、画像用のカラムを用意する必要がない点もActive Storageの特徴の一つです。
1つの添付ファイルの場合
Comment
モデルに1つの画像を添付するには、has_one_attached
を使います。
class Comment < ApplicationRecord
has_one_attached :image
end
:image
はファイルの呼び名で、:photo
、:avatar
、:hoge
など、ファイルの用途に合わせて好きなものを指定してください。ここで、Imageモデルなどを作る必要はないです。Active Storageは裏側でBlob
とAttachment
モデルを使って、こそこそとcomment.image
を使えるようにしてくれます。(有能すぎ)
では、コントローラとビューの中身を書きましょう。
class CommentsController < ApplicationController
def new
@comment = Comment.new
end
def create
@comment = Comment.create params.require(:comment).permit(:content, :image) # POINT
redirect_to @comment
end
def show
@comment = Comment.find(params[:id])
end
def edit
@comment = Comment.find(params[:id])
end
def update
@comment = Comment.find(params[:id])
@comment.update params.require(:comment).permit(:content, :image) # POINT
redirect_to @comment
end
end
<%= form_with model: @comment, local: true do |form| %>
<%= form.text_area :content %><br>
<%= form.file_field :image %><br>
<%= form.submit %>
<% end %>
<% if @comment.image.attached? %>
<%= image_tag @comment.image %>
<% end %>
ほとんど典型的なコードですね。ポイントはcreate
とupdate
アクションのところで、このようにして、image
があたかもComment
のカラムであるかのように扱うことで、フォームのfile_field
で選択された画像をComment
オブジェクトと紐付けます。これも、Active Storageの特徴の一つです。
create
とupdate
アクションを使えない場合は、@comment.image.attach(params[:comment][:image])
で画像を後からComment
オブジェクトと紐付けることもできます。
例えば、画像の選択を任意にしたい場合は以下のようなコードになります。
@comment = Comment.create params.require(:comment).permit(:content)
if image = params[:comment][:image]
@comment.image.attach(image)
end
画像を表示するにはimage_tag
に @comment.image
を渡すだけです。
複数の添付ファイルの場合
複数の添付ファイルを使いたい場合は、上記のコードを少しだけ変更します。
-
has_one_attached
の代わりにhas_many_attached
を使う -
comment.image
の代わりにcomment.images
を使う -
file_field
にmultiple: true
を追記して、複数ファイルの選択を許可
class Comment < ApplicationRecord
has_many_attached :images
end
class CommentsController < ApplicationController
def new
@comment = Comment.new
end
def create
@comment = Comment.create params.require(:comment).permit(:content, images: [])
redirect_to @comment
end
def show
@comment = Comment.find(params[:id])
end
def edit
@comment = Comment.find(params[:id])
end
def update
@comment = Comment.find(params[:id])
@comment.update params.require(:comment).permit(:content, images: [])
redirect_to @comment
end
end
<%= form_with model: @comment, local: true do |form| %>
<%= form.text_area :content %><br><br>
<%= form.file_field :images, multiple: true %><br>
<%= form.submit %>
<% end %>
<% if @comment.images.attached? %>
<% @comment.images.each do |image| %>
<%= image_tag image %> <br>
<% end %>
<% end %>
一枚の時同様、@comment.images.attach(params[:comment][:images])
も使えます。
ファイルの保存先の変更
ファイルの保存先は、各環境の設定ファイルに記載します。
まずは、 config/environments/development.rb
と production.rb
の中身を覗いてみましょう。
# ...
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
# ...
# ...
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
# ...
初期状態では、開発環境(development)、本番環境(production)ともに保存先は :local
に設定されています。
この local
とは、 config/storage.yml
で定義された保存先の名前です。
これを変更するには、:local
のところを :amazon
, :google
, :microsoft
のいづれかと置き換え、config/storage.yml
の方に、必要な認証情報などの値を入力します。
今度は、config/storage.yml
の中身を覗いてみましょう。
test:
service: Disk
root: <%= Rails.root.join("tmp/storage") %>
local:
service: Disk
root: <%= Rails.root.join("storage") %>
# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
# 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: us-east-1
# bucket: your_own_bucket
# Remember not to checkin your GCS keyfile to a repository
# google:
# service: GCS
# project: your_project
# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
# bucket: your_own_bucket
# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
# microsoft:
# service: AzureStorage
# storage_account_name: your_account_name
# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
# container: your_container_name
先ほど見た保存先の local
は、使用するサービスが Disk
(ローカルディスク)に設定れていて、railsアプリ直下の/storage
ディレクトリがファイルの保存先に指定されています。
あと、親切に、コメントがたくさんついてますね。この中から適切なところのコメントを解除することで、好きなストレージサービスを使うことができます。
また、お使いのサービスのgemをGemfileに追記する必要があります。これは、aws-sdk-s3
, google-cloud-storage
, azure-storage
のいづれかになります。
なお、アクセスキーは、セキュリティ上、Rails Credentialsを使って入力するのが好ましいです。
これは、APIキーなどの機密情報を暗号化して保存するための機能で、実はこれもRails 5.2で追加された新機能です。これまでのsecrets.yml
の代わりになります。
<%= Rails.application.credentials.dig(...) %>
の部分は、まさにCredentialsに入力されたデータを読み込んでいますのでコメントをそのまま使いましょう。
- Credentialsにデータを入力・編集するには
$ rails credentials:edit
と叩きます。
エラーが出た場合は、$ EDITOR=vim rails credentials:edit
で、お使いのエディターを指定してください。例:vim
,emacs
,atom
,code
,subl
- 入力した内容は
config/master.key
を用いて暗号化され、config/credentials.yml.enc
が生成されます。 - 復号された中身は
$ rails credentials:show
で確認できます。
Amazon S3 を使う場合
S3は基本的に、ファイルをアップロードし、そのURLを取得できる、AWSのストレージサービスです。
🔰 Amazon S3でバケットを用意する方法
バケットは画像などのファイルをアップロードできる入れ物です。
AWSコンソール → サービス → ストレージ → S3 → 「バケットを作成する」
項目 | 入力・選択 |
---|---|
パケット名 | 例: my-rails-app-bucket |
リージョン | 例: ap-northeast-1 (東京) |
パブリックアクセス許可を管理する | このバケットに読み取りアクセス権限をする |
上記以外 | 全部デフォルトのまま |
🔰 Amazon S3でアクセスキーを作る方法
AWSコンソール → サービス → セキュリティ → IAM → 「ユーザー」 → 「ユーザーを追加」
項目 | 入力・選択 |
---|---|
ユーザー名 | 例: s3user |
アクセスの種類 | プログラムによるアクセス |
「既存のポリシーを直接アタッチ」 → 「S3」で検索 → 「AmazonS3FullAccess」を選択 → 「次へ」 → 「ユーザーの作成」
表示された「アクセスキー ID」と「シークレットアクセスキー」 をメモ帳などにコピペして保管する。(一度しか表示されません)
保存先をAmazon S3に指定する
まず最初に必要なAWS S3のGemをインストールします。
gem "aws-sdk-s3", require: false
Gemfile変更後、$ bundle install
と叩きます。
次に、保存先をAmazon S3に指定します。
# 本番環境(production)の保存先を:localから:amazonに変更
config.active_storage.service = :amazon
# 開発環境(development)でAmazon S3の動作を確認したい場合はこちらの方も変更しましょう
config.active_storage.service = :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) %>
# 以下の2行を変更
region: ap-northeast-1 #東京の場合
bucket: my-rails-app-bucket #自分で作成したS3のバケットの名前
前にも述べましたが、<%= Rails.application.credentials.dig(...) %>
の部分は、Credentialsの情報を読み込んでいます。早速、CredentialsにAmazon S3へのアクセスキーを入力しましょう。
$ EDITOR=vim rails credentials:edit
上の例ではVimが開きます。Atomをお使いの場合、EDITOR=atom
としてください。
VSCodeなら、EDITOR=code
、SublimeTextならEDITOR=subl
。
aws:
access_key_id: 123 #ここに自分のアクセスキーIDをコピペ
secret_access_key: 456 #ここに自分のシークレットアクセスキーをコピペ
Vim: i
で入力開始、esc
→ ZZ
で終了
入力した内容は$ rails credentials:show
で確認できます。
以上で保存先をAmazon S3に変更できました。
重要
Credentialsをお使いの場合は、config/credentials.yml.enc
の中身を復号化するために必要なconfig/master.key
ファイルを本番環境にも配置する必要があります。しかし、master.key
はセキュリティ上、Gitで管理してはいけないため、標準で .gitignore されてあり、PaaSなどのGitリポジトリをそのままデプロイする本番環境(Herokuなど)の場合は、master.key
の中身をコピーして、環境変数 RAILS_MASTER_KEY
として用意する必要があります。
ちなみに、Herokuをお使いの場合は $ heroku config:set RAILS_MASTER_KEY=123
で環境変数を用意できます。
参考
https://www.engineyard.com/blog/active-storage
https://qiita.com/yatmsu/items/08b95e837ac7f24fb443
https://afreshcup.com/home/2017/07/06/introduction-to-active-storage-part-3-google-cloud-storage