Rails
paperclip

Paperclipでファイル保存時にダイジェスト値も保存する

同じ画像をなるべく重複して保存しないように、ファイルのダイジェスト値を計算してDBに保持しておこうと思って、少しハマったのでメモ。

最初こう書いていたが、これだとうまくいかない。

class ProfileImage < ActiveRecord::Base
  has_attached_file :image, styles: {}
  validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/

  before_save :set_digest

  private

  def set_digest
    self.digest = Digest::SHA512.file(image.path)
  end
end

既に保存されているProfileImageに対してProfileImage.all.each(&:save)する分にはこれでよいが、新規保存時はidが未確定なのでimage.pathが定まらずDigest::SHA512.file(image.path)でファイルが開けず落ちてしまう。保存前のtempfileのパスが欲しい。
Paperclipのドキュメントに記述が無かったのでソースに当たってみると、Paperclip::Attachment#staged_pathというそれっぽいものが見つかった。
https://github.com/thoughtbot/paperclip/blob/7edb35a2a9a80c9598dfde235c7e593c023fc914/lib/paperclip/attachment.rb#L176-L180

profile_image.image.staged_pathで所望のファイルパスを得られたので、下記のように書き換えてようやくやりたかったことができた。

  def set_digest
    self.digest = Digest::SHA512.file(image.staged_path || image.path)
  end

あとこれだとProfileImageが更新される度にファイルアクセスが発生してしまうので、同じくPaperclip::Attachmentに生えている#staged?を使えば画像更新時のみにダイジェストをセットするようにできそうだ。