ひよっこエンジニアの雑多な日記

なんとかWeb系のエンジニアをやっています。

【Rails】ActiveStorageを使ってお手軽にファイルアップロードを試す

最近ぼちぼちアウトプットしようとせこせこインプットしています。

前回Rails5.2でたからActionCable試すという舐めた記事を書きましたが、今日はRails5.2で標準搭載されるようになったActiveStorageを試します!

ActiveStorageとは

ActiveStorageはRails5.2から標準で搭載されるようになったファイルアップローダーです。
AWS S3などのクラウドストレージへのアップロードも設定で手軽に実装できる代物です。

rails/activestorage at master · rails/rails · GitHub

今日はこの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を利用すれば複数画像を扱うこともできます。

詳しくはこちらで

Active Storage の概要 | Rails ガイド

ActionStorageを使うための記載はだいたい終了なので、あとはざっと画面などを作成します。

# config/routes.rb
Rails.application.routes.draw do
  root "users#index"
  resources :users, only: [:new, :create]
end
# app/controllers/users_controller.rb
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
<!-- app/views/users/new.html.erb -->
<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 %>
<!-- app/views/users/index.html.erb -->
<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>

ここまでやると、こんな感じになります。

  • 投稿画面 f:id:kimuraysp:20180420235321p:plain

  • 一覧画面 f:id:kimuraysp:20180420235349p:plain

という感じですごく簡単に画像アップロードが実装できてしまいます!

S3にアップロードできるようにする

S3でバケットを作る

せっかくなのでS3にアップロードできるようにしてみます。

まずはAWS S3で適当な名前のバケットを作成します。
(わかりやすくactive-storage-sampleというバケットを作りました)

f:id:kimuraysp:20180420235619p:plain

作成したら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のawsaccess_key_idを取得するという記載になります。

ここまで設定できれば画像を保存した際にローカルではなくS3にファイルが格納されるようになっています。
クラウドストレージへのアップロードも少ない手順で実現できてしまうのはお手軽で素晴らしいですね。

ちなみに

config/master.keyRAILS_MASTER_KEYの取り扱い間違えて消してしまったりすると、credentialsの複合ができなくなり詰んでしまうので注意しましょう。
詰んでしまった場合はconfig/credentials.yml.encを削除してrails credentials:editで1から設定し直すと持ち直せる可能性があります(個人開発に限る。チーム開発だとハラキリしかないかもしれない)

最後に

ActiveStorage手軽で便利なのですが、バリデーションやキャッシュの機能を持ち合わせていないとどこかでみたのでアップロード時にファイルチェックが必要な要件があったりすると扱いにくいかもしれません。

ただ厳密さが求められないような状況であれば素早く実装できるので検討してみても良さそうです!