はじめに
この記事は、プログラミング初学者が他の記事を参考にしたり、実際に実装してみたりして、アウトプットの一環としてまとめたものです。内容に不備などあればご指摘いただますと幸いです。
この記事では、以下を解説しています。
- Active Storage で保存した画像の URL を取得する方法
- variant(リサイズ)した画像をフロントエンドで表示する方法
- API として画像の URL を返却する方法
ActiveStorageの準備
ActiveStorageが未導入の場合は以下を実行します。
$ rails active_storage:install
$ rails db:migrate
画像処理をするため、image_processingを入れます。
Gemfile に下記を記述し、bundle install します。
gem 'image_processing', '~> 1.2'
Rails7以降にvariantでimage_magickを使う場合は
デフォルトの設定がvipsになっているため
config/application.rbに下記を記述します。
config.active_storage.variant_processor = :mini_magick
モデルの定義
下記を記述することで、ActiveStorage の URL を取得できます。
class Tweet < ApplicationRecord
belongs_to :user
has_one_attached :image
# 画像のURL変換メソッド(リサイズあり)
def image_url(width, height)
resize_image = image.variant(resize_to_fill: [width, height]).processed
Rails.application.routes.url_helpers.rails_representation_url(resize_image, only_path: false)
end
# 画像のURL変換メソッド(リサイズなし)
def image_url
Rails.application.routes.url_helpers.rails_blob_url(image, only_path: false)
end
end
ポイント
has_one_attached :image
- 1レコードに1つの画像を紐づけられる
variant
- 画像をリサイズ・加工するActiveStorageの機能
-
resize_to_fillは指定サイズで中央トリミングされる
processed
-
variantの後ろに、processedメソッドをつけると既に同じ加工を施したデータがないかを確認し、存在する場合はそれを呼び出します
Rails.application.routes.url_helpers
- Rails の ルーティングヘルパーを直接呼び出すためのオブジェクト
- URLヘルパーをビューやコントローラ以外の場所で使う場合に必要
rails_representation_url
- variantを含む画像のURLを生成
rails_blob_url
- 画像の絶対URLを生成
only_path: false
- URL を 絶対URL にするかどうか
-
true→/rails/...のような パスだけ -
false(もしくは省略) →http://localhost:3000/rails/...のような 絶対URL
補足
Railsは 用途別にURLヘルパ を用意しています。
- blob(Active Storageを使用し、リサイズなし) →
rails_blob_url - variant(Active Storageを使用し、リサイズあり) →
rails_representation_url - view内だけ →
image_tag/url_for
ルーティング設定
URL生成には ホスト情報 が必要です。(only_path: false の場合、必須)
下記を記述します。
Rails.application.routes.default_url_options[:host] = 'http://localhost:3000'
本番環境でも同様に設定します。
コントローラで画像URLを返す
APIとしてTweetを返す例です。
module Api
module V1
class TweetsController < ApplicationController
def index
tweets = Tweet.includes(image_attachments: :blob)
render json: tweets.map { |tweet|
{
id: tweet.id,
content: tweet.content,
image: tweet.image_url(500, 270)
}
}
end
end
end
end
ポイント
Tweet.includes(image_attachments: :blob)
-
tweetsテーブルから全てのデータを取得します -
includesで N+1クエリを防ぎます
フロントエンドでの表示例
{tweet.image && <img src={tweet.image} />}
レスポンスで返却された画像URLを src 属性に設定し、
画像が存在する場合のみ表示されるようにしています。
おわりに
最後まで読んでいただきありがとうございました。
参考にしたサイト