Railsアプリをデプロイするとbackground: url(bg-hero-001.jpg)が表示されなくなる話

  • 36
    Like
  • 0
    Comment
More than 1 year has passed since last update.

Combinatorのデザインをアップデートした際に、なぜかProduction環境にデプロイすると一部の画像が読み込めないという問題に直面しましたので、原因究明と解決までにやったことを纏めておきたいと思います。

解決前の状態

app/assets/stylesheets/application.css.scss
background: url(bg-hero-001.jpg) no-repeat center;

画像の配置場所
app/asset/images/bg-hero-001.jpg

問題の画面

■Develop環境(bg-hero-001.jpgが表示されている)

localhost:3000/projects
スクリーンショット 2014-12-29 18.18.28.png

■Production環境(bg-hero-001.jpgが表示されていない)

combinator.jp/projects
スクリーンショット 2014-12-29 18.18.13.png

開発中のDevelop環境では確かに画像が表示されていたのに、Production環境にでデプロイするまで、問題に気づくことが出来ませんでした。この原因は、AssetPipelinという仕組みが関係していることが分かりました。まず、Asset Pipelineの説明から。

AssetPipelineとは?

スクリーンショット 2014-12-30 17.02.44.png

みなさんapp/assets以下にcssや、js、画像ファイルといったアセットを配置しているかと思います。これらを「①高速に」「②キャッシュに悪さされないように」本番に反映させるのが、Asset Pipelineの役割です。

①高速に

こちらは、今回の画像の表示には関係ないのですが、asset以下に配置されている様々なcssやjsファイルをapplication.cssやapplication.jsにコンパイル・統合・圧縮して効率的に読み込みまっせ!という仕組みです。(はしょってます笑)

②キャッシュに悪さされないように

画像が表示されないのは、これが原因でした。皆さんapplication.cssやbg-hero.pngといったようにファイルに名前をつけるかと思います。しかし、アップデートしていくと、ファイル名は前と変わっていないけど中身は更新されているということがありますよね。それがキャッシュによって、更新済みのものではなく、更新前の同じファイル名のものが読み込まれちゃうという事態を防ぐのがこの仕組みです。

具体的には、app/assets以下に配置されたファイルが、コードが更新されるごとにファイル名にユニークな拡張子がつけられるようになります。これをdigestというようです。

例えば、

(デプロイ前)bg-hero-001.png
(デプロイ後)bg-hero-001-○○○○.png

さらに、デプロイされると
app/assets以下のファイルは全て
public/assetsに上記のdigest付きになって移動されるのです。

つまり、

(デプロイ前)app/assets/bg-hero-001.png
(デプロイ後)public/assets/bg-hero-001-○○○○.png

これがデプロイ前のDevelop環境ではbg-hero-001.pngが読み込めて、デプロイ後のProduction環境では、bg-hero-001.pngが読み込めない理由でした。(bg-hero-001-○○○○.pngになっているので)

解決方法

①image-urlやimage_tagといったRailsメソッドで画像を呼び出す

urlを指定するのではなく、Railsが指定しているメソッドを使うことで、デプロイ時にapp/assetsからpublic/assetsにファイルが移動する際に、自動的にファイルにdigestを付与してくれるようになります。

app/assets/stylesheets/application.css.scss
background: image-url(bg-hero-001.jpg) no-repeat center;

画像の配置場所
app/asset/images/bg-hero-001.jpg

②public以下に画像を配置する

app/assetsからpublic/assetsに移動する際に、digestが付与されるので初めかpublic/assets以下にファイルを置いておくことで、digestが付与されなくなり、デプロイ前後両方ともで、ファイルを表示できるようになります。しかし、これによって、AssetPipelineの強みが生かされなくなってしまいます。

app/assets/stylesheets/application.css.scss
background: url(/images/bg-hero-001.jpg) no-repeat center;

画像の配置場所
public/images/bg-hero-001.jpg

今回は、画像を表示させたいという目的があったので、②の解決策のデメリットとしては「キャッシュに悪さされないように」という強みが無効化されてしまうことでした。ですが、画像ファイルの場合は、ファイル名を同じままで内容だけ更新することは無いだろうという判断で、②の解決策を取ることで、本番環境でも画像を表示させることに成功しました。

■Production環境(bg-hero-001.jpgが表示されている)

スクリーンショット 2014-12-30 16.33.08.png

Railsのドキュメントでは、基本的にはAsset Pipelineを利用するように推奨されているので、railsメソッドを利用して呼び出していく習慣をつけた方が良いのかもしれませんが、今回は②のような対策方法で画像を表示させることに成功しました。

何か間違っていることなどがありましたら、指摘いただけると嬉しいです。

p.s.
まだまだ未熟なサービスですが、
プロジェクト単位で仲間を集める「Combinator」も宜しくお願いします!