学習記録

アウトプット用に作りました

Active Storage

Actrive Storageとは

Actrive Storageとは、ファイルアップロードを行うための機能です。この機能を使うことでフォームで画像の投稿機能などを簡単に作ることができます。
また画像などのファイルのアップロードを簡単にするメソッドが使用でき、画像を保存するテーブルを簡単に作成できます。

Amazon S3Google Cloud Storage、Microsoft Azure Storageなどの クラウドストレージサービスへのファイルのアップロードや、ファイルをActive Recordオブジェクトにアタッチする機能を提供します。development環境とtest環境向けのローカルディスクベースのサービスを利用できるようになっており、ファイルを下位のサービスにミラーリングしてバックアップや移行に用いることもできます。

Active Storage の概要 - Railsガイド


Active Storageの使用方法

$ rails active_storage:install
$ rails db:migrate

このマイグレーションによって、active_storage_blobsactive_storage_attachmentsという2つのテーブルが生成されます。

  • active_storage_blobsはファイル名、ファイルの種類、バイト数、誤り検出符号などのメタデータを保持するモデル
  • active_storage_attachmentsはBlobオブジェクトとActive Recordオブジェクトを紐付けるための中間テーブル

なおここで作成された2つのモデルはActive Storageを使う際に触れることはありません。なのでカラムの追加など行うこともありません。

事前にTweetモデルを作成しておき、このTweetモデルに画像を添付できるような実装を行っていきます。
※ Active Storageでは、モデルを作成する際に画像用のカラムを用意する必要はありません。

次のコードのようにhas_one_attachedを使うことで、Tweetモデルに画像を一枚添付できるようになりました。

class Tweet < ApplicationRecord
  has_one_attached :image
end

:imageはファイルの名前で、好きなように変更することもできます。(例えば :avatar:photoなど)

以下のように書くことでTweetに画像をつけることができるようになります。

tweet_controller.rb

class TweetController < ApplicationController
  def create
    user = Tweet.create!(tweet_params)
    redirect_to root_path
  end

  def show
    @tweet = Tweet.find(params[:id])
  end

  private
    
  def tweet_params
    params.require(:tweet).permit(:body, :image)
  end
end

tweet投稿フォーム。フォームのfile_fieldで選択された画像をTweetモデルと紐付けています。

new.html.erb

<%= form_with model: @tweet, local: true  do |f| %>
  <%= f.text_area :body %>
  <%= f.file_field :image %>
  <%= f.submit %>
<% end %>

tweet詳細画面。image.attached?で特定のtweetがimageを持っているかどうかを調べられます。

show.html.erb

<% if @tweet.image.attached? %>
  <%= image_tag @tweet.image %>
<% end %>


Actice Storageの保存先の設定

ファイルの保存先は各環境の設定ファイルに記載します。利用するサービスをActive Storageに認識させるには必要な記述です。

config/environments/development.rb

Rails.application.configure do
  config.active_storage.service = :local
end
config/environments/test.rb

Rails.application.configure do
  config.active_storage.service = :test
end

Active Storageのサービスはconfig/storage.ymlで宣言します。アプリケーションが使うサービスごとに、名前と必要な構成を指定します。
開発環境(local)、テストともにDiskサービス(ローカルのディスク)を使うと宣言します。またtmp/storageディレクトリがファイルの保存先に指定されています。

config/storage.yml

local:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>


RSpecテストで画像を添付する

事前にテストで使う画像をspec/fitures/imageにセットしておきます。

それでは実際に「作成済みのtweetに画像を添付できるか」というテストを書いてみます。
FactoryBottweetとuser、またLoginモジュールを作成している前提です。

tweets_spec.rb

require 'rails_helper'

RSpec.describe "Tweets", type: :system do
  let!(:tweet) { create :tweet }
  let(:user) { create :user }

  describe '画像投稿機能' do
    context '画像を添付する' do
      it '正常に添付することができる' do
         Login_as(user)
         visit tweet_path(tweet)
         attach_file 'tweet[image]', "#{Rails.root}/spec/fixtures/image/hoge.jpg"
         click_on '投稿する'
         expect(page).to have_selector("img[src$='hoge.jpg']")
      end
    end
  end

attach_fileを使うことで画像を添付するという操作をすることができます。