Detect MIME type of uploaded file in Ruby
Ruby on-RailsRubyFile UploadSwfuploadRuby on-Rails Problem Overview
Is there a bullet proof way to detect MIME type of uploaded file in Ruby or Ruby on Rails? I'm uploading JPEGs and PNGs using SWFupload and content_type
is always "application/octet-stream"
Ruby on-Rails Solutions
Solution 1 - Ruby on-Rails
The ruby-filemagic gem will do it:
require 'filemagic'
puts FileMagic.new(FileMagic::MAGIC_MIME).file(__FILE__)
# => text/x-ruby; charset=us-ascii
This gem does not look at the file extension at all. It reads a bit of the file contents and uses that to guess the file's type.
Solution 2 - Ruby on-Rails
In Ruby on Rails you can do:
MIME::Types.type_for("filename.gif").first.content_type # => "image/gif"
Solution 3 - Ruby on-Rails
You can use this reliable method base on the magic header of the file :
def get_image_extension(local_file_path)
png = Regexp.new("\x89PNG".force_encoding("binary"))
jpg = Regexp.new("\xff\xd8\xff\xe0\x00\x10JFIF".force_encoding("binary"))
jpg2 = Regexp.new("\xff\xd8\xff\xe1(.*){2}Exif".force_encoding("binary"))
case IO.read(local_file_path, 10)
when /^GIF8/
'gif'
when /^#{png}/
'png'
when /^#{jpg}/
'jpg'
when /^#{jpg2}/
'jpg'
else
mime_type = `file #{local_file_path} --mime-type`.gsub("\n", '') # Works on linux and mac
raise UnprocessableEntity, "unknown file type" if !mime_type
mime_type.split(':')[1].split('/')[1].gsub('x-', '').gsub(/jpeg/, 'jpg').gsub(/text/, 'txt').gsub(/x-/, '')
end
end
Solution 4 - Ruby on-Rails
The ruby-filemagic gem is good solution, but requires additional dependencies on libmagic (recently removed from CarrierWave as part of CarrierWave::MagicMimeTypes removal).
If you're interested in a pure ruby implementation, consider the MimeMagic gem! It works well for file types listed in the freedesktop.org mime database:
require 'mimemagic'
MimeMagic.by_magic(File.open('Table-Flip-Guy.jpg')).type # => "image/jpeg"
For Microsoft Office 2007+ formats (xlsx, docx, and pptx), require the overlay (unless you're okay with the generic "application/zip" MIME type for these files)
require 'mimemagic'
require 'mimemagic/overlay'
MimeMagic.by_magic(File.open('big_spreadsheet.xlsx')).type # => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
Solution 5 - Ruby on-Rails
filemagic gem is good solution but depends lots of unnecessary gems. (rails, aws-sdk-core, ...)
If your app is small and only runs in Linux or OSX, consider to use file
program:
require 'shellwords'
mimetype = `file --brief --mime-type - < #{Shellwords.shellescape(__FILE__)}`.strip
Note: Replace __FILE__
with any expr contains the filepath.
Solution 6 - Ruby on-Rails
mimemagic gem will also do it
https://github.com/minad/mimemagic
from the oficial documentation
> MimeMagic is a library to detect the mime type of a file by extension > or by content. It uses the mime database provided by freedesktop.org > (see http://freedesktop.org/wiki/Software/shared-mime-info/). > > require 'mimemagic' > MimeMagic.by_extension('html').text? > MimeMagic.by_extension('.html').child_of? 'text/plain' > MimeMagic.by_path('filename.txt') > MimeMagic.by_magic(File.open('test.html')) > # etc...
Solution 7 - Ruby on-Rails
As of 2021, I would claim that the best tool to compute mime types based on all the available hints (magic number, file name when the magic number does not suffice, user hints) is Marcel.
To shamelessly quote the documentation itself:
Marcel::MimeType.for Pathname.new("example.gif")
# => "image/gif"
File.open "example.gif" do |file|
Marcel::MimeType.for file
end
# => "image/gif"
Marcel::MimeType.for Pathname.new("unrecognisable-data"), name: "example.pdf"
# => "application/pdf"
Marcel::MimeType.for extension: ".pdf"
# => "application/pdf"
Marcel::MimeType.for Pathname.new("unrecognisable-data"), name: "example", declared_type: "image/png"
# => "image/png"
Marcel::MimeType.for StringIO.new(File.read "unrecognisable-data")
# => "application/octet-stream"
Solution 8 - Ruby on-Rails
in case you are doing this from scratch, install mimemagic gem
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 as mentioned above
%w(JPEG GIF TIFF PNG).include?(MimeMagic.by_magic(result).type)
this might be more elegant
Solution 9 - Ruby on-Rails
You can use
Mime::Type.lookup_by_extension(extention_name)
Thanks