Визначте тип файлу в Ruby


74

Як надійно визначити тип файлу? Аналіз розширення файлу неприйнятний. Повинен бути рубієнний інструмент, подібний до команди файлу UNIX (1)?

Це стосується MIME або типу вмісту, а не класифікацій файлової системи, таких як каталог, файл або сокет.

Відповіді:


58

Існує рубінова прив’язка, libmagicяка робить те, що вам потрібно. Він доступний як самоцвіт з іменем ruby-filemagic :

gem install ruby-filemagic

Вимагати libmagic-dev.

Документація здається трохи тонкою, але з цього слід почати:

$ irb 
irb(main):001:0> require 'filemagic' 
=> true
irb(main):002:0> fm = FileMagic.new
=> #<FileMagic:0x7fd4afb0>
irb(main):003:0> fm.file('foo.zip') 
=> "Zip archive data, at least v2.0 to extract"
irb(main):004:0> 

Згідно з grub.ath.cx/filemagic/CHANGELOG, цей камінь, схоже, не підтримується активно.
Ларс Хогсет

23
Я радий повідомити, що цей самоцвіт знову активно підтримується github.com/blackwinter/ruby-filemagic
Мартін Карпентер

Працює і на Windows.
chris finne

3
Знову ж таки, ця самоцвіт, здається, не підтримується активно. На Github це позначено як "не підтримується" та "прийняти мене" .
tanius

35

Якщо ви працюєте на машині Unix, спробуйте:

mimetype = `file -Ib #{path}`.gsub(/\n/,"")

Мені невідомі жодні чисті рішення Ruby, які працюють так надійно, як "файл".

Відредаговано, щоб додати: залежно від того, яку ОС ви використовуєте, можливо, вам доведеться використовувати "i" замість "I", щоб отримати файл для повернення типу mime.


18
Щоб запобігти неприємному злому, спробуйте використовувати popen:IO.popen(["file", "--brief", "--mime-type", path], in: :close, err: :close).read.chomp
sj26

Так, це або cocaineсамоцвіт.
maletor

8
@ sj26 Кожного разу, коли я телефоную popen, я отримую процес зомбі, оскільки об'єкт вводу-виводу не закритий. Щоб це виправити, використовуйте блок:IO.popen(["file", "--brief", "--mime-type", path], in: :close, err: :close) { |io| io.read.chomp }
Ендрю

1
Інтерполяція @Pete вмісту, який постачає користувач, у рядок команди, як зворотні посилання, є потенційною уразливістю безпеки. Використання popen з масивом аргументів запобігає цій категорії експлуатації. :-)
sj26,

1
Відмінна ідея про зомбі! IO.popen(["file", "--brief", "--mime-type", path], &:read).chompпрацює теж.
sj26

14

Я знайшов обстріл найнадійнішим. Для сумісності як на Mac OS X, так і на Ubuntu Linux я використовував:

file --mime -b myvideo.mp4
відео / mp4; charset = двійковий

Ubuntu також друкує інформацію про відеокодек, якщо може, що є дуже круто:

file -b myvideo.mp4
ISO Media, система MPEG v4, версія 2


6
має бути file -b --mime-type myvideo.mp4для користування Інтернетом
Ям Маркович

9

Ви можете використовувати цю надійну основу методів на магічному заголовку файлу:

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

1
Вам також потрібно шукати "\ xff \ xd8 \ xff \ xdb" як підпис JPEG.
Річард Фейрхерст,

6

Якщо ви використовуєте клас File, ви можете доповнити його такими функціями на основі відповіді @ PatrickRichie:

class File
    def mime_type
        `file --brief --mime-type #{self.path}`.strip
    end

    def charset
        `file --brief --mime #{self.path}`.split(';').second.split('=').second.strip
    end
end

І, якщо ви використовуєте Ruby on Rails, ви можете залишити це в config / initializers / file.rb і доступне у вашому проекті.


4

Це було додано як коментар до цієї відповіді, але насправді має бути власною відповіддю:

path = # path to your file

IO.popen(
  ["file", "--brief", "--mime-type", path],
  in: :close, err: :close
) { |io| io.read.chomp }

Я можу підтвердити, що це спрацювало для мене.


1
Це чудово працює з додатковим бонусом за відсутність необхідності додавати та підтримувати ще одну перлину.
Стівен Хірлстон

2

Ви можете спробувати shared-mime (gem install shared-mime-info). Потрібне використання бібліотеки спільної mime-інформації Freedesktop, але робить як перевірку імені файлу / розширення, так і перевірку "магії" ... спробував дати йому кружлятись зараз, але у мене немає freedesktop shared-mime-info база даних встановлена ​​і повинна виконувати "справжню роботу", на жаль, але це може бути те, що ви шукаєте.


2

Для тих, хто прийшов сюди за допомогою пошукової системи, сучасний підхід до пошуку MimeType в чистому рубіні - це використання мімемагічного каменя.

require 'mimemagic'

MimeMagic.by_magic(File.open('tux.jpg')).type # => "image/jpeg" 

Якщо ви вважаєте, що безпечно використовувати лише розширення файлу, тоді ви можете використовувати самоцвіт mime-типів :

MIME::Types.type_for('tux.jpg') => [#<MIME::Type: image/jpeg>]


1

Нещодавно я знайшов mimetype-fu .

Здається, це найпростіше надійне рішення для отримання типу MIME файлу.

Єдине застереження полягає в тому, що на машині Windows він використовує лише розширення файлу, тоді як у системах на базі * Nix це чудово працює.




-2

Ви можете спробувати MIME :: Types для Ruby .

Ця бібліотека дозволяє ідентифікувати ймовірний тип вмісту файлу MIME. Ідентифікація типу вмісту MIME базується на розширеннях імен файлів.


6
З Readme.txt: "Ідентифікація типу вмісту MIME базується на розширеннях імен файлів". OP прямо вимагає метод на основі аналізу вмісту, а не розширення імені файлу.
Мартін Карпентер,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.