【Rails】Active Storageの基本情報と実装方法

はじめに

Rails 5.1までは、画像などをアップロードするにはCarrierWaveなどのGemを使う必要がありました。Rails 5.2からActiveStorageという機能が追加され、別途Gemをインストールしなくても組み込みの機能だけで画像などをアップロードできるようになりました。

本記事では、ActiveStorageの使い方についてまとめています。

ActiveStorageの使い方

ActiveStorageを使うと、容易に画像ファイルなどをローカルストレージや代表的なクラウドストレージ(Amazon S3、Google Cloud Storage、Microsoft Azure Storageなど)にアップロードすることができます。また、開発環境はローカルストレージ、本番環境はクラウドストレージというように、環境毎にアップロード先を切り分けることもできます。

ActiveStorageの追加

ActiveStorageはRails組み込みの機能ですが、Railsアプリ作成直後の状態では使うことができません。まずはActiveStorageをRailsアプリに追加する必要があります。

$ rails active_storage:install

上記のコマンドを実行すると、active_storage_blobsおよびactive_storage_attachmentsというテーブルを作成するためのマイグレーションファイルが作成されます。ActiveStorageでアップロードしたファイルの情報などが格納されるテーブルです。

$ rails db:migrate

テストサーバーを起動している場合は再起動してください。これでActiveStorageを使うことができるようになりました。

ActiveStorageの実装

アップロードファイルを追加したいモデルとActiveStorageを紐付けます。モデルに以下を追記します。

# 1ファイルの場合
has_one_attached :image

# 複数ファイルの場合
has_many_attached :images

コントローラーのストロングパラメーターに属性を追加します。ビューから渡されたファイルは、通常のカラムのようにsavecreateを実行することで自動でアップロードされます。

class ArticlesController < ApplicationController
  def create
    article = Article.create!(article_params)
    redirect_to article_path(article.id)
  end

  private
    def article_params
      params.require(:article).permit(:title, :content, :image)
    end
end

フォームにファイル選択フィールドを追加します。

<%= form.file_field :image %>

詳細ページにイメージタグを追加します。

<%= image_tag @article.image %>

これでActiveStorageでファイルがアップロードできるようになり、アップロードしたファイルを表示できるようになりました。

ストレージの種類

ActiveStorageはファイルの保存先ストレージをいくつかの中から選択することができます。各ストレージの設定(アクセストークンやバケットなど)はconfig/storage.ymlに記述します。

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

config/storage.ymlに記述したストレージの中から実際にどれを使うかは、環境毎の設定ファイル(config/environments/ディレクトリ配下)に記述します。通常は、開発環境はローカルストレージ、本番環境はクラウドストレージにします。

# ローカルストレージを使用
config.active_storage.service = :local

Amazon S3の設定

Amazon S3の設定は以下の通りです。なお、AWSの設定については記述していません。

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

Gemfileに以下を追記してbundle installを行います。

gem "aws-sdk-s3", require: false

Google Cloud Storageの設定

Google Cloud Storageの設定は以下の通りです。なお、Google Cloud Platformの設定については記述していません。

google:
  service: GCS
  project: your_project
  credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
  bucket: your_own_bucket

キーファイルではなく各パラメーターを記述することもできます。

google:
  service: GCS
  credentials:
    type: "service_account"
    project_id: your_project_id
    private_key_id: <%= Rails.application.credentials.dig(:gcs, :private_key_id) %>
    private_key: <%= Rails.application.credentials.dig(:gcs, :private_key).dump %>
    client_email: your_client_email
    client_id: your_client_id
    auth_uri: "https://accounts.google.com/o/oauth2/auth"
    token_uri: "https://accounts.google.com/o/oauth2/token"
    auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
    client_x509_cert_url: your_client_x509_cert_url
  project: your_project
  bucket: your_own_bucket

Gemfileに以下を追記してbundle installを行います。

gem "google-cloud-storage", "~> 1.8", require: false

Microsoft Azure Storage

Microsoft Azure Storageの設定は以下の通りです。なお、Microsoft Azureの設定については記述していません。

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

Gemfielに以下を追記してbundle installを行います。

gem "azure-storage", require: false

ActiveStorageのバリデーション

ActiveStorageで追加した属性に特化したバリデーションは現在のところ実装されていません。バリデーションは自分で定義することもできるので、ActiveStorageを追加した場合は実装しておきましょう。

自分で定義したバリデーションを呼び出すにはvalidateを使用します。通常のバリデーションvalidatesとは異なるので注意してください。

ファイル種別

選択されたファイルの形式が画像かをチェックするには、以下のように実装します。

validate :file_type

private
  def file_type
    images.each do |image|
      if !image.blob.content_type.in?(%('image/jpeg image/png'))
        errors.add(:images, 'は JPEG 形式または PNG 形式のみ選択してください')
      end
    end
  end

ファイルサイズ

選択されたファイルのサイズをチェックするには、以下のように実装します。

validate :file_size

private
  def file_size
    images.each do |image|
      if image.blob.byte_size > 5.megabytes
        errors.add(:images, 'は 5MB 以下のファイルを選択してください')
      end
    end
  end

ファイルの最大数

選択されたファイルの最大数をチェックするには、以下のように実装します。

validate :file_length

private
  def file_length
    if images.length > 5
      errors.add(:images, 'は 5 ファイルまでにしてください')
    end
  end

画像の変換

アップロードした画像をビューで表示するときに変換することができます。例えば、サムネイル用に100x100程度の小さな画像が必要なとき、自分でリサイズして元の画像とは別にアップロードするというのは面倒です。ActiveStorageのバリアントという機能を使えば、アップロードした画像を基にして簡単にサムネイル用の画像が作成できます。

バリアントを使うにはImageProcessingというGemが必要です。Gemfileに以下を追記してbundle installを行います。テストサーバーを起動している場合は再起動してください。

gem 'image_processing'

ImageProcessingは内部の画像プロセッサとしてMiniMagickというGemを使っています。また、MiniMagickImageMagickというソフトウェアのGem版なので、ImageMagickがインストールされていないと動きません。ややこしいですが、まとめると以下のようになります。

ImageMagick -> MiniMagick -> ImageProcessing

そんなわけで、ImageProcessingを使うためにImageMagickをインストールします。この手順は本番環境でも行う必要があります。

# Macの場合
$ brew install imagemagick

# Ubuntuの場合
$ sudo apt-get install imagemagick

# バージョン確認
$ convert --version

これでバリアントが使えるようになったので、ビューを以下のように変更します。バリアントは画像を表示するときにリサイズするので、好きな場所で好きなサイズの画像を表示できるということです。ただし、リサイズするには多少時間がかかるので使用の際はご注意ください。

<%= image_tag article.image.variant(resize: "100x100") %>

ImgaeMagickで使えるオプションはすべて使えます。

<%= image_tag @user.image.variant(gravity: "Center", resize: "100x100!", crop: "100x100+0+0").processed %>
オプション 説明
gravity 画像変換の始点。convert -list gravityで指定値一覧を表示(デフォルトはNone)。
resize 画像のサイズ。詳しくは Image Geometry - ImageMagick 参照。
crop 始点からの切り抜きサイズ。
precessed 既に変換済みであれば変換しない(キャッシュを表示)。

まとめ

Railsアプリのデプロイ先としてHerokuがよく使われますが、Herokuはデプロイ後にアップロードされたファイルを一定時間後に削除するようになっています。Herokuに限らず、アップロードファイルの保管先はクラウドストレージに委任するのが普通です。

本記事を参考にして、ActiveStorageの使い方を覚えていただければと思います。

関連記事

【Rails】ダウンロードしたフリーフォントをWebpackerを使って導入する方法
# はじめに Webサイトのデザインを彩るひとつの要素にフォントの種類があります。Google Fontsの登場により、様々なフォントが手軽に導入できるようになりました。しかし、世の中にはGoogle Fontsが提供するフォント以外にもたくさん [...]
2021年10月16日 13:26
【Rails】mimemagicに依存しているRailsアプリでbundle installコマンドが失敗する事象の対処法
# はじめに Railsアプリで`bundle install`コマンドを実行しようとしたところ、以下のエラーメッセージが出力されコマンドに失敗しました。 ``` Your bundle is locked to mimemagic (0 [...]
2021年10月14日 14:44
【Rails】macOSのアップデート後にrailsコマンドが失敗する事象の対処法
# はじめに 久しぶりにRailsアプリに手を入れようと思い、`rails server`コマンドを実行してRailsサーバーを立ち上げようとしたところ、エラーが出て起動に失敗してしまいました。 前回までの間にしたことの中で思い当たることと [...]
2021年10月14日 12:11
【Rails】Railsアプリのデバッグ《マルチデバイス篇》
# はじめに 近年のWebアプリはレスポンシブ対応が当たり前になっています。最低でもPCとスマートフォンに対応したデザイン、ときにはその中間のタブレットに対応したデザインなんかも作成する必要があります。 Webアプリの開発はPCを使って行う [...]
2021年5月23日 13:02
【Rails】Railsアプリのデバッグ《Better Errors篇》
# はじめに Railsアプリの開発中になんらかのエラーが発生すると、デフォルトでは以下のような画面が表示されます(画像をクリックすると拡大します)。 <a class="gallery" data-group="gallery" href [...]
2021年5月22日 19:42