A Little Each Day

note to self

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


このように画像は編集せずにそのままなのに、投稿自体を更新してしまったら画像が表示されなくなってしまいました。


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


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

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


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


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

上記の問題を解決するには、ファイル名を時間フォーマットで指定するのではなく、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(文字列)が生成される可能性が天文学的数字の確率でありえないらしいです。(もちろん天文学的確率なので稀にぶつかるらしいです...)。


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


それでは実際に投稿してみましょう。

今回も画像自体は更新せず、Contentだけを編集します。

f:id:chi_kun:20160517181901p:plain


きちんとContentが編集されていて、画像自体は上記と違いきちんと表示されていますね。


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

ぜひ参考にしてみてください。



本日は以上です。