Cory O'Daniel – These are just words Software development, thoughts, and randomness

30Dec/090

Ruby File extensions: Mime types, encodings, file extensions list, mime detection, OH ME OH MY

This is my file class in ruby. I like knowing real file extensions of files that are being uploaded. I'm not keen on trusting the extension that comes with the file. So I wrote this little extension to the file class to get all sorts of mime related data from the file using the 'file' command line utility. Make sure you install that for your operating system of choice. Gives you things like all possible extensions for a given mime type, media type, sub type, and can even correct incorrect or missing extensions.

This uses the mime/types rubygem for getting a list of extensions. Install that first:

1
gem install mime-types

Put this in your core ext somewhere:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
require "rubygems"
require "mime/types"
require "open3"
 
class File
  attr_reader :mime_type, :media_type, :sub_type, :encoding, :extensions
 
  def extensions
    self.mime if @extensions.nil?
    @extensions
  end
 
  def encoding
    self.mime if @encoding.nil?
    @encoding
  end
 
  def sub_type
    self.mime if @sub_type.nil?
    @sub_type
  end
 
  def media_type
    self.mime if @media_type.nil?
    @media_type
  end
 
  def mime_type
    self.mime if @mime_type.nil?
    @mime_type
  end
 
  def mime
    if @mime_type.nil?
      @mime_type = File.sys_file_cmd(File.expand_path(self.path))
 
      unless @mime_type.nil?
        type = MIME::Types[@mime_type].first
 
        @media_type   = type.media_type if type
        @sub_type     = type.sub_type if type
        @encoding     = type.encoding if type
        @extensions   = type.extensions if type
      end
    end
 
    return @mime_type
  end
 
  def File.mime(path)
    File.sys_file_cmd(path)
  end
 
  def correct_extension!    
    File.correct_extension!(self.path)
  end
 
=begin rdoc
  *Name*:: File.correct_extension!
  *Access*:: public, static
  *Description*:: Corrects a files extension
  *Authors*:: Cory ODaniel
  *Last_Edited*:: Wed Feb 13 12:04:11 PST 2008
  ===Parameters
  * Name: file_path
    * Description: Path to file 
    * Datatype: String
    * Default: None
    * Required: True
  ===Returns
  *Description*:: Path to new file or FALSE
  *Datatype*:: String | False
=end
  def File.correct_extension!(file_path)
 
    extensions = MIME::Types[File.mime(file_path)][0].extensions
 
    return false if extensions.nil?
 
    extension = File.extname(file_path) 
    unless extensions.include?(extension)
      directory = File.dirname(file_path)
 
      file_name = File.basename(file_path).split(".")
      file_name.pop if file_name.size > 1 #Remove the bad extension
 
      file_name.push(extensions.first) #just user the first extension in list
      file_name = file_name.join(".")
 
      begin
        new_path = File.join(directory,file_name)
        File.rename(file_path,new_path)
        return new_path
      rescue Exception => e
        return false
      end
    end
  end
 
  private
 
=begin rdoc
  *Name*:: File.sys_file_cmd
  *Access*:: private, static
  *Description*:: Handles call to system "file" command
  *Authors*:: Cory ODaniel
  *Last_Edited*:: Wed Feb 13 12:13:39 PST 2008
  ===Parameters
  * Name: path
    * Description:  file to get mime info for
    * Datatype: String
    * Default: None
    * Required: True
  ===Returns
  *Description*:: Mime information
  *Datatype*:: String | nil
=end  
  def File.sys_file_cmd(path)
    if File.exist? path
      stdin,stdout,stderr = Open3.popen3(%{file --mime -b #{path}})
 
      file_err  = stderr.gets
      file_out  = stdout.gets
 
      if file_err
        raise Exception, "The 'file' command line binary was not found."
      end
 
      if file_err.nil? && (!file_out.nil? && !file_out.empty?)
        return file_out.split(";")[0].strip
      end
    end
 
    return nil
  end
end

Wanna use it?

1
2
3
4
5
6
7
8
9
10
11
12
13
# load the file from above however you wanna do it
File.mime("path/to/your/file.html") => "text/html"
@f = File.open("path/to/your/file.html")
@f.mime #=> "text/html"
@f.extensions #=> ['html', 'htm']
@f.media_type #=> "text"
@f.sub_type #=> "html"
 
@wrong_extension = File.open("path/to/image/with/wrong.extension")
@wrong_extension.correct_extension!
 
# Or as a class method
File.correct_extension!("path/to/a/file/with/a/wrong.extension")

Yay, youve got correct extensions. Congrats.

Post to Twitter Post to Digg Post to Facebook Post to Reddit Post to StumbleUpon

Tagged as: Leave a comment
Comments (0) Trackbacks (0)

No comments yet.


Leave a comment


No trackbacks yet.