LoginSignup
68
54

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-07-03

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

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

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

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

app/uploaders/avatar_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/avatar_uploader.rb
class AvatarUploader < CarrierWave::Uploader::Base
  include CarrierWave::Magic
  process :set_magic_content_type => [true]

  # 〜以下省略〜
end

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

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

app/models/user.rb
  # 〜抜粋〜
  mount_uploader :avatar, AvatarUploader
  validate  :avatar_valid?, :if => Proc.new{ |user| user.avatar_changed? && user.errors[:avatar].blank? }

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

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

68
54
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
68
54