CarrierWaveというgemを使うとRailsアプリケーションに画像アップロード系の機能を割と楽に実装できる。

アップロード先はサーバーローカルだけでなく、AmazonS3やGoogleなどのクラウド系のストレージにも対応しているし、アップロード時にリサイズ画像を用意したりファイル名を変更したりと色々なカスタマイズができるため、画像アップロード系は大体このgemだけでカバーできるというすぐれものだ。

ところで、そのファイル名のカスタマイズでちょっとハマったのでメモしておこうと思う。

使い方おさらい

CarrierWaveの使い方をざっとおさらいしておく。

  1. Gemfileにgem "carrierwave"と書いてbundle install
  2. 画像を持つモデル(Itemとする)をつくる rails generate model Item image:string
  3. アップローダーを作る rails generate uploader ItemImage
  4. Itemモデルにアップローダーを関連付ける

3.で生成されたアップローダーファイルはこんな感じになる。filenameの部分は追記したところで、日時をファイル名にしている。

# encoding: utf-8

class ItemUploader < CarrierWave::Uploader::Base
  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  def filename
    "#{Time.now.strftime("%Y%m%d-%H%M%S")}.png"
  end

end

4.のところではItemモデルにちょっと追記する。

class Item < ActiveRecord::Base
  mount_uploader :image, ItemUploader
end

何が問題だったのか

このやり方だと、画像を更新しなくてもモデルが持っているファイル名が上書きされてしまう!というのが問題だった。

おなじみScaffoldのフォームで見てみる。このようなフォームからItemを生成する。

すると、こんな感じでItemが生成される。

Imageのところに書いてあるPathを<img>タグに渡せばちゃんと画像が表示される。

そしてEdit。

名前だけ変更してファイルは指定なしで更新してみる。すると、画像ファイルのパスが変わってしまっているのである。

ところがその先には画像ファイルはなく、画像は以前のパスのところにある。つまり、画像を変更していないのにパスが変わってしまうのである。

更新がありうる画像ファイルの場合はファイル名を時間に依存したものやランダムなものにするとあとから参照できなくなってしまうことがあるのでダメだよ、というのが今回のお話。

サンプルコード

ruby-sandbox/carrierwave-sample at master · xoyip/ruby-sandbox