さいとブログ

carrierwave with fog-googleで静的ファイルを扱ってみる

carrierwaveとfog-googleでGCSに静的ファイルをアップロードしたり、ローカルと本番でどう切り替えていくかについて、試してみたことを書き連ねていきます。
※特に目新しいことはしておらず、既に出回っている記事以上のことは書いていないと思います

ローカルストレージでcarrierwaveを試してみる

carrierwaveのREADME の手順に沿って、環境構築していきます。

uploaderクラスをこれで生成します。

bin/rails g uploader Avatar


Userテーブルにnameとavatarを保存するカラムを用意します。

bin/rails g model User name:string avatar:string
bin/rails db:migrate


次に公式に従って以下を定義します。

class User < ApplicationRecord
  mount_uploader :avatar, AvatarUploader
end


ここまで公式の手順に従ってやってきました。
これでuserテーブルにavatarのパスを保存できる準備が整います。
rails consoleで試してみます。

bin/rails c

u = User.new
u.name = 'tanaka'
file = File.open('/Users/tanaka/Desktop/hoge.png')
u.avatar = file

u.save!
u.avatar
=>
#<AvatarUploader:0x00000001105974d0
 @cache_id=nil,
 @cache_storage=#<CarrierWave::Storage::File:0x00000001106db170 @cache_called=nil, @uploader=#<AvatarUploader:0x00000001105974d0 ...>>,
 @deduplication_index=nil,
 @file=
  #<CarrierWave::SanitizedFile:0x00000001102dadc8
   @content=nil,
   @content_type=nil,
   @declared_content_type=nil,
   @file="/Users/tanaka/work/project/examples/carrierwave-cdn-gcs-signedurl/public/uploads/user/avatar/1/hoge.png",
   @original_filename=nil>,
 @filename="hoge.png",
 @identifier="hoge.png",
 @model=#<User:0x000000010f1af698 id: 1, name: "tanaka", avatar: "hoge.png", created_at: Sun, 26 Nov 2023 03:16:54.005319000 UTC +00:00, updated_at: Sun, 26 Nov 2023 03:16:54.005319000 UTC +00:00>,
 @mounted_as=:avatar,
 @original_filename=nil,
 @staged=false,
 @storage=#<CarrierWave::Storage::File:0x0000000110678688 @cache_called=nil, @uploader=#<AvatarUploader:0x00000001105974d0 ...>>,
 @versions={}>

u.avatar.url
=> "/uploads/user/avatar/1/hoge.png"
u.avatar.curent_path
=> "/Users/tanaka/work/project/examples/carrierwave-cdn-gcs-signedurl/public/uploads/user/avatar/1/hoge.png"


こんな感じでavatar.{url,current_path}でpathを引っ張ってくることができます。
ローカルだとプロジェクトのpublic/uploads/user/**にファイルが保存されています。

GCSをバックエンドに指定してcarrierwaveを試してみる

公式を参考にdevelopmentとproductionでcarrierwaveのstorageを:file:fogに切り替えるようにする。

CarrierWave.configure do |config|
  if %w(staging production).include?(Rails.env)
    config.fog_provider = 'fog/google'
    config.fog_credentials = {
      provider: 'Google',
      google_project: Rails.application.credentials.google_project,
      google_json_key_string: Rails.application.credentials.google_json_key_string,
      # google_json_key_location: '',
    }
    config.fog_directory = Rails.application.credentials.gcs_bucket
    config.fog_public = nil
    config.storage = :fog
    config.cache_storage = :fog
  else
    config.storage = :file
  end
end


こんな感じでinitializers/carrierwave.rbに定義します。
あとはproduction用のdbは適当にローカルで用意してやれば、consoleでファイルアップロードが試せるはずです。

bin/rails c

u = User.new
u.name = 'hoge'
file = File.open('/Users/hoge/Desktop/hengao_mabuta_uragaesu.png')
u.avatar = file
u.save!

u.avatar
=>
#<AvatarUploader:0x0000000110f6d5e0
 @cache_id=nil,
 @deduplication_index=nil,
 @file=
  #<CarrierWave::Storage::Fog::File:0x0000000112a4bba0
   @base=#<CarrierWave::Storage::Fog:0x0000000110f6c4d8 @uploader=#<AvatarUploader:0x0000000110f6d5e0 ...>>,
   @content_type=nil,
   @path="uploads/user/avatar/1/hengao_mabuta_uragaesu.png",
   @uploader=#<AvatarUploader:0x0000000110f6d5e0 ...>>,
 @filename=nil,
 @identifier="hengao_mabuta_uragaesu.png",
 @model=#<User:0x0000000112559390 id: 1, name: "hoge", avatar: "hengao_mabuta_uragaesu.png", created_at: Sun, 26 Nov 2023 07:57:37.885654000 UTC +00:00, updated_at: Sun, 26 Nov 2023 07:57:37.885654000 UTC +00:00>,
 @mounted_as=:avatar,
 @staged=false,
 @storage=#<CarrierWave::Storage::Fog:0x0000000110f6c4d8 @uploader=#<AvatarUploader:0x0000000110f6d5e0 ...>>,
 @versions={}>

u.avatar.url
=> "https://storage.googleapis.com/{bucket name}/uploads/user/avatar/1/hengao_mabuta_uragaesu.png?GoogleAccessId={service account email}&Signature=Pgi0Erp517jf%2B37ns9nWcdgNppU1H2ieZJ6Ls59GvzRlrVqMuYSToTI3el6g%0AUv%2FuSzm%2BpxOAL34kTgX7vlk1jAlRiC4SHL80ShLXi3ZomaEbnGOdspFquxXU%0AsL03%2FdL8h3eQOOb6hvf3GH3eY74QMbhQoJjtS0gLX0yQ2l9zADqgKBaNMg9F%0A9RSwZAPcgObO%2BuuYktmvJlOpA4qt7k2K2spCT24dgbhz99TP1gWO8NBocHV%2B%0Ax9DOXGHoe6w0MZUaBssLGaoYu572qf%2FSBYmUPRjc5a2TNSf5%2BPyQqL7FoCDY%0ARWm4c5FEB21M9Lo6sKRLvYXtpn1Q6oXbjhIsdUScvA%3D%3D&Expires=1700987087"

u.avatar.path
=> "uploads/user/avatar/1/hengao_mabuta_uragaesu.png"


このようにRails.envを切り替えることでローカルストレージかGCSかを切り替えることが容易にできるみたいです。
urlやpathも返すことができるので、このstorage.googleapis.comのurlを使ってReactなどのフロント側は、fetchすれば画像表示も容易に実装できると思います。
補足ですが、今回の検証に使っているGCSは非公開に設定しているため、署名がない限り参照できないようになっています。

最後に

今回使用したコードはこちらです。
https://github.com/ksaito422/carrierwave-cdn-gcs-signedurl

プロフィール

profile icon

saitoです。
ソフトウェアエンジニアとして働いています。
web開発に関する学びを当ブログに書き残しています。