スポンサーリンク
背景
現在Ruby on Rails6で作成したアプリをHerokuの無料プランで運用しており、
1. 画像のアップロードはActive Storageを使用してGCS(Google Cloud Storage)に行っている。
2. DBはPostgresを使用しているが、無料プランで使用できるのは10,000レコードまで。
3. Active StorageとActive Recordを使用すると、裏でDBにテーブルとレコードが作られる。
4. 画像を多くアップロードすると、テーブルのレコードが大量に消費されてしまい、レコード数の上限に達してしまう。
通常の使用例(1つのActive Recordにavatarなどで画像を保存する形)であれば、そんなに問題なら無いかもしれないが、
今回はajaxで画像を1つ1つ保存し、何件もアップロードを行うため、大量に消費してしまうために問題となる。
なので、無料枠で使用可能なように極力テーブルのレコード数を減らすために、画像のアップロード機能をActive StorageとActive Recordを使用しない方法に変更する必要がある。
スポンサーリンク
方法
CarrierWaveというgemを使用する。
これはActive Storageが登場する前から存在していて、多くの画像アップロード処理で使用されているデファクトスタンダードみたいなライブラリである。
もちろんS3やGCSなどのクラウドストレージにも対応している。
GEM
下記のgemをgemfileに記載して、bundle installでインストールする。
画像の圧縮などの加工をしないならばmini_magickは不要。
1 2 3 4 |
# for gcs gem 'carrierwave' gem 'fog-google' gem 'mini_magick' |
実装
Uploader
CarrierWaveのお作法に沿って、Uploaderクラスを実装する。
1. mini_magicによる画像のリサイズ
2. 本番環境以外はローカルに画像を保存する
ようにしている。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class ImageUploader < CarrierWave::Uploader::Base include CarrierWave::MiniMagick process resize_to_fit: [700, 700] # test env upload loacal if Rails.env.development? storage :file elsif Rails.env.test? storage :file else storage :fog end end |
ActiveModelによる実装
CarrierWaveのよくあるサンプルだと、上記のUploaderをActive Recordのメンバーに実装する。
この場合だと、DBにレコードが保存されてしまうため、今回の目的に合致しない。
調べたところ、Active Recordでは無くActive Modelに実装することができることがわかった。
下記のように、storeメソッドを呼び出すことで画像が保存可能になる。
1 2 3 4 5 6 7 8 9 10 11 |
class ImageAttachment include ActiveModel::Model extend CarrierWave::Mount attr_accessor :image mount_uploader :image, ImageUploader def save(image) self.image.store!(image) end end |
CGS設定
GCSのバケットの設定を行うために、config/initializersディレクトリ以下にcarrierwave.rbを作成して設定を行う。
Active Storageで使用していた環境変数がそのまま使用できて便利であった。
1 2 3 4 5 6 7 8 9 10 |
CarrierWave.configure do |config| config.fog_provider = 'fog/google' config.fog_credentials = { provider: 'Google', google_project: ENV['GCS_PROJECT'], google_json_key_string: ENV['GOOGLE_CREDENTIALS'].as_json } config.fog_directory = ENV['GCS_BUCKET'] end CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/ |
CGSのバケット設定
デフォルトの画像保存先は、uploadsディレクトリになるため、
そのディレクトリに公開設定を行う必要がある。
公開設定に関しては下記のオフィシャルドキュメントの通りに実施すれば良い。
https://cloud.google.com/storage/docs/access-control/making-data-public?hl=ja
ちなみに、Uploaderのstore_dirメソッドをオーバーライドすることで保存先等は変えられる模様。
実装
後はControllerでこれらを呼び出すだけ。
今回は、ajaxで画像の保存だけを行うAPIとして実装を行なっていたので下記のように実装した。
1 2 3 4 5 6 |
# POST adamin/article_edit/attach def attach attachment = ImageAttachment.new attachment.save(ajax_params) render json: { filename: attachment.image.url } end |
スポンサーリンク
まとめ
以上で、Active StorageとActive Recordを使用せずにCarrierWaveへの処理の置き換えができた。
不要なテーブルレコード数を消費せずに済んで満足。