WEBサービス創造記

WEBサービスを作ったり保守したりしてる人のメモブログです。

CarrierWaveでファイルの内容をもとにcontent-typeの判定を行う

      2015/05/19

拡張子をもとに判定することの問題

ファイルのアップロードを行うアプリを実装する際に、ファイルのcontent-typeを判定したいことがある。

もっとも手軽にcontent-typeの判定を行う方法としては拡張子での判定がある。CarrierWaveではuploaderクラス内でextension_white_listメソッドを上書きすることで、ホワイトリスト形式でアップロードを許可する。

app/uploaders/avator_uploader.rb

  # 〜抜粋〜

  # Add a white list of extensions which are allowed to be uploaded.
  # For images you might use something like this:
  def extension_white_list
    %w(jpg jpeg gif png)
  end

しかし、拡張子だけをもとに判定した場合は内容はPDFだけど拡張子が.jpgになっているようなファイルの内容と拡張子に相違があるファイルがバリデーションを通過してアップロードできてしまう。
このようなケースを防ぎたい時は、ファイルの内容をもとにcontent-typeを判定する必要がある。

ファイルの内容をもとに判定を行う方法

“ruby-filemagic”というGemがある。このGemではファイルの内容をもとにcontent-typeの判定を行うことができる。
ruby-filemagicの簡単な使用方法は以下の通りである。

まず、インストールを行う。

Gemfile

gem 'ruby-filemagic'
bundle install

続いて、rails console上で適当なファイルに対してcontent-typeの判定を行ってみる。

at_console.rb

require "filemagic"
FileMagic.new(FileMagic::MAGIC_MIME).file(File.join("/path/to/files/actually_pdf.jpg"))  # ※actually_pdf.jpgは、内容はPDFだけど拡張子が.jpgのファイル
=> "application/pdf; charset=binary"

このように、拡張子にとらわれずファイルの内容によってcontent-typeが判定できる。

このruby-filemagicをCarrierWaveで利用できるようにするGemで”carrierwave-magic”というのがある。これを利用すると掲題の目的を達成することができる。
carrierwave-magicの使用方法(の一例)は以下の通りである。

まず、インストールを行う。

Gemfile

gem 'carrierwave-magic'
bundle install

続いてアップローダでcarrierwave-magicを利用できるように設定する。

app/uploaders/avator_uploader.rb

class AvatorUploader < CarrierWave::Uploader::Base
  include CarrierWave::Magic
  process :set_magic_content_type => [true]

  # 〜以下省略〜
end

基本的にはcarrierwave-magicのREADMEにあるusageに従った形だが、set_magic_content_typeの第一引数にtrueを渡すことで必ずファイル内容をもとにcontent_typeの判定が行われるようになる(※詳しくはcarrierwave-magicのソースを参照)。

これでAvatorUploaderのインスタンスではcontent_typeにファイル内容をもとにチェックした値が入るようになった。
あとはその値をもとに任意のバリデーションを行う。

app/models/user.rb

  # 〜抜粋〜
  mount_uploader :avator, AvatorUploader
  validate  :avator_valid?, :if => Proc.new{ |user| user.avator_changed? && user.errors[:avator].blank? }

  def avator_valid?
    if avator.file.content_type != "image/jpeg"
      errors.add(:avator, "不正なファイルが添付されています。")
    end
  end

また、この方法は拡張子がついていないファイルのcontent-typeを確認したいときなどにも利用できる。

※同じ内容の記事をQiitaにも投稿しました。
CarrierWaveでファイルの内容をもとにcontent-typeの判定を行う

 - Ruby on Rails , , , , ,