sjsc
sjsc

Reputation: 4632

Ruby on Rails: How do you check if a file is an image?

How would you check if a file is an image? I'm thinking you could use an method like so:

def image?(file)
  file.to_s.include?(".gif") or file.to_s.include?(".png") or file.to_s.include?(".jpg")
end

But that might be a little inefficient and not correct. Any ideas?

(I'm using the paperclip plugin, btw, but I don't see any methods to determine whether a file is an image in paperclip)

Upvotes: 22

Views: 15820

Answers (7)

d1jhoni1b
d1jhoni1b

Reputation: 8025

I honestly think this is way easier, use mimemagic gem:

First install it

gem 'mimemagic'

Open stream(bytes of target image)

url="https://i.ebayimg.com/images/g/rbIAAOSwojpgyQz1/s-l500.jpg"
result = URI.parse(url).open

Then check data-stream's file type

For example:

MimeMagic.by_magic(result).type == "image/jpeg"

Even though this might be more elegant

%w(JPEG GIF TIFF PNG).include?(MimeMagic.by_magic(result).type)

Upvotes: 3

joelparkerhenderson
joelparkerhenderson

Reputation: 35453

One approach is to use the "magic number" convention to read the first bits of a file.
http://www.astro.keele.ac.uk/oldusers/rno/Computing/File_magic.html

Examples:

  "BM" is a Bitmap image
  "GIF8" is a GIF image
  "\xff\xd8\xff\xe0" is a JPEG image

Example in Ruby:

  def bitmap?(data)
    return data[0,2]=="MB"
  end

  def gif?(data)
    return data[0,4]=="GIF8"
  end

  def jpeg?(data)
    return data[0,4]=="\xff\xd8\xff\xe0"
  end

  def file_is_image?(filename)
    f = File.open(filename,'rb')  # rb means to read using binary
    data = f.read(9)              # magic numbers are up to 9 bytes
    f.close
    return bitmap?(data) or gif?(data) or jpeg?(data)
  end

Why use this instead of the file name extension or the filemagic module?

To detect the data type before writing any data to disk. For example, we can read upload data stream before we write any data to disk. If the magic number doesn't match the web form content type, then we can immediately report an error.

We implement our real-world code slightly differently. We create a hash: each key is a magic number string, each value is a symbol like :bitmap, :gif, :jpeg, etc.

Upvotes: 14

Tony Dong
Tony Dong

Reputation: 11

As an addition to Joel's answer, in Rails 5 I had to transform the comparison string to a bytecode. Eg:

def jpeg?(data)
  return data[0,4]=="\xff\xd8\xff\xe0".b
end

Upvotes: 1

Rafeeq
Rafeeq

Reputation: 231

Please check it once

MIME::Types.type_for('tmp/img1.jpg').first.try(:media_type)
=> "image"

MIME::Types.type_for('tmp/img1.jpeg').first.try(:media_type)
=> "image"

MIME::Types.type_for('tmp/img1.gif').first.try(:media_type)
=> "image"

MIME::Types.type_for('tmp/ima1.png').first.try(:media_type)
=> "image"

Upvotes: 17

Sinan Ünür
Sinan Ünür

Reputation: 118128

I would use the ruby-filemagic gem which is a Ruby binding for libmagic.

Upvotes: 16

Peter Brown
Peter Brown

Reputation: 51697

Since you're using Paperclip, you can use the built in "validates_attachment_content_type" method in the model where "has_attached_file" is used, and specify which file types you want to allow.

Here's an example from an application where users upload an avatar for their profile:

has_attached_file :avatar, 
                  :styles => { :thumb => "48x48#" },
                  :default_url => "/images/avatars/missing_avatar.png",
                  :default_style => :thumb

validates_attachment_content_type :avatar, :content_type => ["image/jpeg", "image/pjpeg", "image/png", "image/x-png", "image/gif"]

The documentation is here http://dev.thoughtbot.com/paperclip/classes/Paperclip/ClassMethods.html

Upvotes: 7

BandsOnABudget
BandsOnABudget

Reputation: 3976

imagemagick has a command called identity that handles this - check w/ the paperclip documentation - there's probably a way to handle this from within your RoR app.

Upvotes: 1

Related Questions