【Ruby on Rails】画像は public と app/assets/images のどちらに設置すべき?

概要

Rails ではアプリケーション内で使用される画像の設置場所に、現在 2つの場所が存在します 。

  1. public ディレクトリ
  2. app/assets/images ディレクトリ

どちらに設置しても画像の読み込みに困りませんが、設置場所によってどんな違いがあるのか、今回調べてみました。

 

画像を読み込む際のパスが違う

まず、設置場所によって画像を読み込む際のパスが異なります。

public ディレクトリに配置した画像を読み込む場合、パスを / から始める必要があります。

<%= image_tag('/tennis_ball.png') %>
# 生成される img タグの中身
# <img src="/tennis_ball.png" alt="Tennis ball" />

 

app/assets/images ディレクトリに設置した画像を読み込む場合、パスを / から始める必要はありません。

<%= image_tag('tennis_ball.png') %>
# 生成される img タグの中身
# <img src="/assets/tennis_ball-aa455d01913a920bcfe8749e327219bd4caaca41f7ce325fc5edba8e46372843.png" alt="Tennis ball" />

生成される img タグの中身を見て下さい。

パスを / から始めたかどうかで、src 値が大きく異なることが分かります。

また、app/assets/images ディレクトリに画像を設置した場合、src 値の画像ファイルパスに MD5 ハッシュ値が挿入されていることが分かります。

この MD5 ハッシュ値は、フィンガープリント と呼ばれるもので、アセットパイプラインで使われます。

 

アセットパイプラインについて

Rails では app/assets ディレクトリ以下は、アプリケーション自身が保有するアセットの置き場として奨励されており、アセットパイプラインの対象となります。

アセットパイプラインとは、JavaScript や CSS の アセット を最小化(スペースや改行を詰める、コメントを削除するなど)または圧縮して 1 つのファイルに連結するためのフレームワークです。

Rails ではこの機能がデフォルトで ON になっています。

ファイルを連結して 1 つにすることで、ブラウザからサーバへのリクエスト数を減らすことができ、ページの読み込み時間が大きく短縮されます。

また圧縮することによっても、ファイルサイズが小さくなり、ブラウザからの読み込み時間が短縮されます。

ただし、画像ファイルはバイナリデータなので、アセットの最小化や圧縮の話はあまり関係ない(効果がない)かなと思っています。

画像ファイルは連結してくれません。画像ファイルを 1 つに連結するとか無理です。

アセットパイプラインはファイル名にフィンガープリントを挿入し、アセットファイルがブラウザでキャッシュされるようにしています。

アセットファイルの中身が少しでも変更されると、フィンガープリントが自動で更新されて、ブラウザでキャッシュされていた既存のアセットが無効になります。

この話は、画像ファイルを扱う場合に、大きく関係しています。

画像ファイルを同じ名前で新しい画像に更新した場合、ブラウザにキャッシュされた画像が残っていると、新しい画像が表示されず、キャッシュされた画像が表示されてしまいます。

これを回避するにはファイル名を変更するか、ブラウザのキャッシュを削除してもらうかしかありません。

app/assets/images ディレクトリに設置した画像は、同じ名前で違う画像に差し替えても、画像ファイルの中身からフィンガープリントを自動で更新して、画像ファイル名に挿入するので、ブラウザでキャッシュされた古い画像が表示される心配がありません。

ただし、config.assets.digest = true の場合のみです。(デフォルト true)

参考:

railsguides.jp

 

CSS で使えるプロパティが違う

CSS で使えるプロパティが違うのは production モードの話です。

development モードで開発していて、いざ本番リリースをしようと production モードに切り替えたら背景画像が読み込めなかった経験がありませんか?

自分は初めて Rails を触った時に結構苦戦しました。。

まず、production モードでプリコンパイルされたファイルは public/assets に置かれます。

プリコンパイルされたファイルは、Webサーバによって静的なアセットとして扱われます。

以下の設定を変更して、Webサーバから静的ファイルを読み込めるようにする必要があります。

$ vim config/environments/production.rb
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
config.public_file_server.enabled = true # true に変更

参考:Configuring Rails Applications — Ruby on Rails Guides

 

CSS から画像を読み込む際に background プロパティを利用することがあると思います。画像の設置場所によって、使える値が異なります。

public ディレクトリに設置した画像は、以下の CSS の書き方で背景に画像を適用できます。

  • background: url('画像パス')
  • background: image-url(’画像パス')
  • background-image: url('画像パス')
  • background-image: image-url('画像パス')

app/assets/images ディレクトリに設置した画像は、sass-rails の image-url ヘルパーを使わないと背景に画像を適用できません。

  • background: image-url(’画像パス')
  • background-image: image-url('画像パス')

ちなみに、image-url('画像パス') は url(/assets/画像パス) に変換され、image-path('画像パス') は '/assets/画像パス' に変換されます。

 

public ディレクトリに置くケース

もし、同じアプリケーション内で JavaScript を利用しており、JavaScript 内で画像を扱うのであれば、app/assets/images ディレクトリの画像はフィンガープリントが付いてしまうので(画像の URL が変わってしまうので)扱いにくいです。

こういう場合は public ディレクトリに置くしかないと思います。(体験談)

 

まとめ

画像の設置場所による違いをまとめました。

自分が見つけた違いは 3 つです。(他にもあるかも)

  1. 画像を読み込む際のパス
  2. ブラウザのキャッシュ対策
  3. production モードで使える CSS プロパティ

特に大きかったのは、app/assets ディレクトリに画像を設置した場合、アセットパイプラインの対象となり、色々な恩恵を受けることができることです。

アプリケーション自身が保有するアセットの置き場として奨励されていますし、app/assets/images ディレクトリに画像を設置するのが正しいのではないかと思いました。

特に理由がない場合は、app/assets/images ディレクトリに画像を設置することをお勧めします。