确定Ruby中的文件类型


74

如何可靠地确定文件的类型?文件扩展名分析是不可接受的。是否必须有一个类似于UNIX file(1)命令的rubyesque工具?

这与MIME或内容类型有关,与文件系统分类(例如目录,文件或套接字)无关。

Answers:


58

红宝石绑定可以满足libmagic您的需求。它可以作为名为ruby-filemagic的gem获得

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的说法,该gem似乎没有得到积极维护。
拉尔斯·豪格斯09年

23
我很高兴地报告,该宝石再次得到积极维护,github.com / blackwinter / ruby
Martin Carpenter 2010年

也可以在Windows上使用。
克里斯·芬妮

3
再说一次,这个瑰宝似乎没有得到积极维护。在Github上标记为“ unmaintain”和“ adopt-me” 。
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对象未关闭。要解决此问题,请使用以下代码块:IO.popen(["file", "--brief", "--mime-type", path], in: :close, err: :close) { |io| io.read.chomp }
Andrew

1
@Pete将可能由用户提供的内容插值到诸如反引号的命令字符串中是一个潜在的安全漏洞。将popen与参数数组一起使用可防止此类利用。:-)
sj26'6

1
关于僵尸的好点!IO.popen(["file", "--brief", "--mime-type", path], &:read).chomp也可以。
sj26'6

14

我发现炮击是最可靠的。为了兼容Mac OS X和Ubuntu Linux,我使用了:

file --mime -b myvideo.mp4
视频/ mp4;字符集=二进制

Ubuntu也会打印视频编解码器信息(如果可以的话),这很酷:

file -b myvideo.mp4
ISO媒体,MPEG v4系统,版本2


6
应该是file -b --mime-type myvideo.mp4对网络使用情况
山药Marcovic

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
这与不需要添加和维护另一个gem的额外好处完美配合。
史蒂文·希尔斯顿

2

您可以尝试共享mime(gem安装shared-mime-info)。需要使用Freedesktop shared-mime-info库,但同时进行文件名/扩展名检查和“魔术”检查...尝试自己尝试一下,但我没有freedesktop shared-mime-info不幸的是,数据库已安装并且必须做“实际工作”,但这可能正是您要的。


2

对于那些被搜索引擎带到这里的人来说,一种现代的在纯红宝石中找到MimeType的方法是使用模仿性宝石。

require 'mimemagic'

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

如果您觉得仅使用文件扩展名是安全的,则可以使用mime-types gem:

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


1

我最近发现了mimetype-fu

这似乎是获取文件的MIME类型的最简单可靠的解决方案。

唯一需要注意的是,在Windows计算机上,它仅使用文件扩展名,而在基于* Nix的系统上,它的效果很好。




-2

您可以尝试使用MIME :: Types for Ruby

该库允许识别文件的可能的MIME内容类型。MIME内容类型的标识基于文件的文件扩展名。


6
从Readme.txt中:“ MIME内容类型的标识基于文件的文件扩展名”。OP明确要求基于内容分析而不是文件扩展名的方法。
马丁·卡彭特
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.