Edited at

メモ:ActiveStorageで格納した画像をCloudfront配信する


この記事は


  • Rails5.2からActiveStorageという添付ファイル系のライブラリが入っています

  • これがオフィシャルな割にCDN対応しておらず結構微妙です

  • 性能的にCloudfrontで配信したかったので、若干試行錯誤したのでメモです


ActiveStorageの流れ

AWSと連携する場合、だいたい下記のような流れで利用されます。


ファイルアップロード


  • ブラウザからアップロードファイルを受け付け

  • AWSのAPI経由でS3にファイルを配置

  • と同時に、そのファイルのパスだとかファイルタイプ、サイズだとかのメタ情報をRails内のテーブルに保持


ファイルダウンロード


  • 格納しておいたメタ情報から、表示したいファイルのリンクを作成


    • このリンクはパーマネントなもの

    • Railsサーバを指している



  • ブラウザからRailsへ画像表示のリクエストを投げる

  • RailsはAWSのAPI経由で5分間限定のS3へアクセス可能なURLを払い出し、そこへのリダイレクトをブラウザへ返却


ActiveStorageの問題点


  • 上の流れでまずいのは、ActiveStorageでアップロードしたファイルを参照するには都度Railsを経由する必要があるという点です

  • つまり100枚の画像を表示する場合、瞬間的に100回Railsへアクセスが行くという事になります(あかん!)

  • 5分ごとにリンクをexpiredさせたいような、非常にセンシティブな情報(電子書籍とかね)をアップロードするサービスであれば、この方が都合がいいのかもしれませんが、そうでもないシステムにはきつい仕様です


公式な対応


対応方針

で、対応内容ですがベタです


  • (A)CloudfrontでS3バケットを参照できるようにします


    • Cloudfrontのサブドメインが払い出されます



  • (B)Rails上でS3上のファイル名を割り出します



    • User.has_one_attached :photoだとすると、user.photo.blob.keyの値がファイル名です

    • ちなみにS3ではこのファイル名で、バケット直下にずらーーーーーっと並んでます(男らしい)



  • そして(A)+(B)のURLに参照すればアクセス可能です


    • cloudfrontのサブドメインがxxxで、ファイル名がXXXXXXXであれば、 https://xxx.cloudfront.net/XXXXXXX




対応詳細


事前準備


  • 事前にActiveStorageはaws連携で使えるようにしておいてください

  • また、アップロード先のS3バケットをCloudfrontで公開状態にして、URLでアクセスできるようにしておいてください(詳細は割愛)


普通の画像表示


  • 通常こんな感じでActiveStorage経由のURLを埋め込むと思います

  • これの代替となるヘルパを作ります


erb


<img src="<%= rails_blob_path user.photo %>" />


ヘルパの準備


  • ActiveStorageの設定ファイルはこんな感じとします


storage.yml

local:

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

amazon:
service: S3
access_key_id: <%= ENV['S3_ACCESS_KEY'] %>
secret_access_key: <%= ENV['S3_SECRET_KEY'] %>
region: ap-northeast-1
bucket: <%= ENV['S3_BUCKET'] %>



  • こんな感じのヘルパを追加します

  • 上記storage.ymlのキーで分岐して、開発環境には影響が出ないようにしています


xxx_helper.rb

  def cdn_ready_blob_path(attachment)

service = Rails.application.config.active_storage.service
if service == :local
# 元々のヘルパ
rails_blob_path(attachment)
elsif service == :amazon
# S3上でのファイル名を取得してURLを組み立てる
key = attachment&.blob&.key
"#{Settings.cloudfront.url}/#{key}"
end
end


  • SettingsだとかでCloudfrontで発行されたドメインを環境別に定義しておきます

  • ステージングとか環境でCloudfrontのdistributeを分けることができます


settings/production.yml

cloudfront:

url: 'https://xxx.cloudfront.net'


CDN対応した画像表示


  • このヘルパを使ってさっきのビューを書き直します


erb


<img src="<%= cdn_ready_blob_path user.photo %>" />


これで


  • Cloudfrontから直接ファイルが配信できるようになりました!


終わりに


  • 今後も公式のライブラリとしてやってくのならオフィシャルにCDN対応してほしいですねー!

  • ちなみにこの辺しっかりしてたライブラリのpaperclipはActiveStorageに譲ってdeprecatedという・・・