LoginSignup
27
26

More than 3 years have passed since last update.

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

Last updated at Posted at 2019-08-14

この記事は

  • 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という・・・
27
26
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
26