読者です 読者をやめる 読者になる 読者になる

ちーくんのブログ

プログラミング備忘録

carrierwaveで更新した時に画像が表示されない問題を解決する

こんにちは。本日はcarrierwaveを使用して画像をアップロードしている場合、投稿を編集して更新した時に画像が表示されない問題について説明したいと思います。

特定の条件でファイル名を命名している(タイムスタンプや同じファイル名など)とこの問題が現れるので解決策を共有したいと思います。

バージョン

Ruby '2.2.1'
Rails '4.2.1'


ファイル名を時間や同じにしている場合

carrierwaveではファイルを保存する時にデフォルトではアルファベットや数字しか許可していません。そしてファイル名に日本語が入っていると嫌なのでファイル名を投稿した時間(タイムスタンプ)などにしている方も多いと思います。
しかしながら、この方法だと投稿はできても編集ができない場合があります。

これはどういうことかと申しますと、例えば画像は更新しないけど内容を編集したいなと思い更新すると画像のパスまで更新されてしまいます。すると今まで表示されていた画像が表示されなくなり、更新が失敗したと思われてしまいます。


ちょっとわかりづらいので例をみてみましょう。

# application.rb
  <!-- タイムゾーン -->
  config.time_zone = 'Tokyo'
# picture_uploader.rb
  <!-- ファイル名を時間に変更 -->
  def filename
    "#{Time.now.strftime("%Y%m%d-%H%M%S")}.jpg"
  end

普通に投稿...

f:id:chi_kun:20160517153425p:plain

普通に投稿できてます。


次に投稿を編集します。
画像はそのままContent(内容)を編集してみます。
すると...

f:id:chi_kun:20160517153522p:plain

あれ?画像が表示されない!
画像は編集してないのになんで?


これは上記でも説明したように投稿を更新してしまうとモデルに紐付いた画像のパスまでもが更新されてしまうのでその先に画像のパスがないので画像が表示されなくなってしまいます。


ちなみに同じファイル名でも同様の現象が起きるようです。

1 . demo.jpgを投稿
2 . demo.jpg(全く同じ名前の別のファイル)
3 . キャッシュが残っているため画像が更新されていない
(実際は更新できている。)


これだと自分も困るし、ユーザーも困ると思うのでこの問題をクリアするために解決策をみていきましょう。

ファイル名をランダムにしよう

上記の問題を解決するには、ファイル名を時間に指定するのではなくuuidを使用してファイル名をランダムな文字列で作成して保存してしまいます。

# picture_uploader.rb

  <!-- 文字列をランダムにする -->
  def filename
    "#{secure_token}.#{file.extension}" if original_filename.present?
  end

  protected
  def secure_token
    var = :"@#{mounted_as}_secure_token"
    model.instance_variable_get(var) or model.instance_variable_set(var, SecureRandom.uuid)
  end

ここで使われているuuidは僕は専門外なので詳しくはわかりませんが、簡単に言えば同じid(文字列)が生成される可能性が天文学的数字の確率でありえないらしいです。(もちろん天文学的確率なので稀にぶつかるらしいです...)。


これでファイル名がそれぞれユニークなものになり編集しても画像が表示されるようになります。

それでは実際に投稿してみましょう。
普通に投稿して編集して更新すると...

f:id:chi_kun:20160517181901p:plain

きちんと画像が編集されていて表示されていますね。


もしあなたがタイムスタンプやデフォルトで画像を保存していて画像が表示(更新)されないなと思ったら、上記の方法で解決される可能性があります。ぜひ参考にしてみてください。



本日は以上です。