最近ぼちぼちアウトプットしようとせこせこインプットしています。
前回Rails5.2でたからActionCable試すという舐めた記事を書いたので今回はちゃんとActiveStorageを試します!
ActiveStorageとは
ActiveStorageはRails5.2から標準で搭載されるようになったファイルアップローダーです。
AWS S3などのクラウドストレージへのアップロードも設定で手軽に実装できる代物です。
今日はこのActiveStorageを使って簡単な画像も投稿できる機能を作ります。
導入
まずは適当にrails newでサンプルアプリプロジェクトをつくります。
(DBにpostgresql使いますがなんでもOKです)
$ rails new active_storage_sample -d postgresql
プロジェクトができたらGemfile内のmini_magickのコメントアウトを外します。
画像処理のために導入するためです。
# Use ActiveStorage variant
gem 'mini_magick', '~> 4.8' # ここのコメントアウト外す
コメントアウトを外したらbundle installでインストールしておきます。
これで導入に必要なものは整いました。
ActiveStorageをインストールする
ActiveStorageを使うためにはまずインストールのコマンドを実行します。
$ rails active_storage:install
実行するとActiveStorageでアップロードファイルを管理するテーブルのmigrationファイルが生成されるのでrails db:migrateを実行しておきましょう。
生成されるテーブルは以下の二つになります。
active_storage_blobs
| カラム名 | 保存内容 | 
|---|---|
| id | ID | 
| key | ファイルを一意に識別するkey | 
| filename | アップロードしたファイルの名前 | 
| content_type | ファイルの種類 | 
| metadata | メタデータ(画像なら縦横の大きさなどが格納される) | 
| byte_size | ファイルサイズ(byte単位) | 
| checksum | チェックサム | 
| created_at | 作成日時 | 
active_storage_attachments
| カラム名 | 保存内容 | 
|---|---|
| id | ID | 
| name | モデルの属性名(Userモデルのavatarとか) | 
| record_type | モデル名(Userとか) | 
| record_id | record_typeのモデルのID | 
| blob_id | active_storage_blogsのID | 
| created_at | 作成日時 | 
ActiveStorageの設定を確認する
ファイルの格納先はconfig/storage.ymlで設定されています。
test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>
local:
  service: Disk
  root: <%= Rails.root.join("storage") %>
例えばlocalの設定を使えばプロジェクトルートにあるstorageディレクトリ配下にファイルを格納するという意味になります。
次にconfig/environments/development.rbをみてみます。
# Store uploaded files on the local file system (see config/storage.yml for options)
config.active_storage.service = :local
という記載があり格納先にconfig/storage.ymlで定義したlocalの設定を使うよという設定が書いてあります。
これで設定の確認は完了です。
ファイルアップロードする画面を作る
まずは画像アップロードできるようにUserモデルを作ります。
$ rails g model user name:string
ActiveStorageを扱う際は画像アップロード用のカラムを定義する必要はありません。
コマンドを実行したらrails db:migrateを実行します。
Userモデルができたら、ActiveStorageを使うための記述を追記しておきます。
class User < ApplicationRecord
  has_one_attached :avatar # 追記
end
この追記を行うことでUserモデルにavatarという画像を扱う属性が付与されます。
has_oneがあればhas_manyもあります。
has_many_attachedを利用すれば複数画像を扱うこともできます。
詳しくはこちらで
ActionStorageを使うための記載はだいたい終了なので、あとはざっと画面などを作成します。
Rails.application.routes.draw do
  root "users#index"
  resources :users, only: [:new, :create]
end
class UsersController < ApplicationController
  def index
    @users = User.all
  end
  def new
    @user = User.new
  end
  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to root_url, notice: 'Add User'
    else
      render :new
    end
  end
  private
  def user_params
    params.fetch(:user, {}).permit(:name, :avatar)
  end
end
<h1>Add User</h1>
<%= form_with model: @user, local: true do |form| %>
  <div>
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>
  <div>
    <%= form.label :avatar %>
    <%= form.file_field :avatar %>
  </div>
  <div>
    <%= form.submit %>
  </div>
<% end %>
<h1>Users Index</h1>
<%= link_to 'Add user', new_user_path %>
<ul>
  <% @users.each do |user| %>
    <li>
      <p><%= user.name %></p>
      <%= image_tag user.avatar %>
    </li>
  <% end %>
</ul>
ここまでやると、こんな感じになります。
という感じですごく簡単に画像アップロードが実装できてしまいます!
S3にアップロードできるようにする
S3でバケットを作る
せっかくなのでS3にアップロードできるようにしてみます。
まずはAWS S3で適当な名前のバケットを作成します。
(わかりやすくactive-storage-sampleというバケットを作りました)
作成したらS3を操作できるIAMユーザーを作成してアクセスキーとシークレットキーを取得しておきましょう。
格納先の設定をS3に向くように変更する
config/storage.ymlの以下のコメントアウトを外します。
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: active-storage-sample
config/environments/development.rbで先ほどlocalとなっていることを確認した設定をamazonに変更します。
config.active_storage.service = :amazon # :localを:amazonに変更
またS3へのアップロードを行うためにaws-sdk-s3を導入します。
Gemfileに以下を追記しましょう。
gem 'aws-sdk-s3'
追記したらbundle installしておきます。
アプリケーションからS3につなぎこむ
アプリケーションからS3につなぎこむためにIAMユーザーを作成して取得したアクセスキーとシークレットキーを設定しましょう。
Rails5.2からcredentialsという機密情報を暗号化する機能が追加されています。
これを使ってアクセスキーとシークレットキーを設定します。
$ EDITOR=vim rails credentials:edit
これを実行するとエディタ(この記載だとvim)が開いてキーを設定できます。
エディタが開いたら以下のように設定を記載します。
aws:
  access_key_id: アクセスキー
  secret_access_key: シークレットキー
記載したら保存して閉じます。
するとconfig/credentials.yml.encというファイルとconfig/master.keyというファイルができています。
config/credentials.yml.encは暗号化されているので内容を見ることはできません。
暗号化のキーにはconfig/master.keyに記載されているキーが使用されています。(環境変数RAILS_MASTER_KEYが設定されていれば、これを使う)
設定を確認したい場合はrails credentials:showを実行すれば生のデータを確認することができます。
先ほどconfig/storage.ymlでコメント外したところに
Rails.application.credentials.dig(:aws, :access_key_id)
という記載がありましたが、これはcredentialsのawsのaccess_key_idを取得するという記載になります。
ここまで設定できれば画像を保存した際にローカルではなくS3にファイルが格納されるようになっています。
クラウドストレージへのアップロードも少ない手順で実現できてしまうのはお手軽で素晴らしいですね。
ちなみに
config/master.keyやRAILS_MASTER_KEYの取り扱い間違えて消してしまったりすると、credentialsの複合ができなくなり詰んでしまうので注意しましょう。
詰んでしまった場合はconfig/credentials.yml.encを削除してrails credentials:editで1から設定し直すと持ち直せる可能性があります。(チーム開発だとツラみがすごいかも…)
最後に
ActiveStorage手軽で便利なのですが、バリデーションやキャッシュの機能を持ち合わせていないとどこかでみたのでアップロード時にファイルチェックが必要な要件があったりすると扱いにくいかもしれません。
ただ厳密さが求められないような状況であれば素早く実装できるので検討してみても良さそうです!


