1
1
module Paperclip
2
2
class ContentTypeDetector
3
+ # The content-type detection strategy is as follows:
4
+ #
5
+ # 1. Blank/Empty files: If there's no filename or the file is empty, provide a sensible default
6
+ # (application/octet-stream or inode/x-empty)
7
+ #
8
+ # 2. Uploaded file: Use the uploaded file's content type if it is in the list of mime-types
9
+ # for the file's extension
10
+ #
11
+ # 3. Standard types: Return the first standard (without an x- prefix) entry in the list of
12
+ # mime-types
13
+ #
14
+ # 4. Experimental types: If there were no standard types in the mime-types list, try to return
15
+ # the first experimental one
16
+ #
17
+ # 5. Unrecognized extension: Use the file's content type or a sensible default if there are
18
+ # no entries in mime-types for the extension
19
+ #
20
+
3
21
EMPTY_TYPE = "inode/x-empty"
4
22
SENSIBLE_DEFAULT = "application/octet-stream"
5
23
6
24
def initialize ( filename )
7
25
@filename = filename
8
26
end
9
27
28
+ # Returns a String describing the file's content type
10
29
def detect
11
30
if blank_name?
12
31
SENSIBLE_DEFAULT
13
32
elsif empty_file?
14
33
EMPTY_TYPE
15
- elsif !match?
16
- type_from_file_command
17
- elsif !multiple?
18
- possible_types . first
34
+ elsif best_from_possible_types
35
+ best_from_possible_types . content_type
19
36
else
20
- best_type_match
37
+ type_from_file_command || SENSIBLE_DEFAULT
21
38
end . to_s
22
39
end
23
40
@@ -30,23 +47,25 @@ def empty_file?
30
47
def blank_name?
31
48
@filename . nil? || @filename . empty?
32
49
end
50
+
51
+ def empty?
52
+ File . exists? ( @filename ) && File . size ( @filename ) == 0
53
+ end
33
54
34
55
def possible_types
35
56
@possible_types ||= MIME ::Types . type_for ( @filename )
36
57
end
37
-
38
- def match?
39
- possible_types . length > 0
58
+
59
+ def official_types
60
+ @official_types ||= possible_types . reject { | type | type . content_type . match ( / \/ x-/ ) }
40
61
end
41
-
42
- def multiple?
43
- possible_types . length > 1
62
+
63
+ def types_matching_file
64
+ possible_types . select { | type | type . content_type == type_from_file_command }
44
65
end
45
-
46
- def best_type_match
47
- types_matching_file = possible_types . select { |type | type . content_type == type_from_file_command }
48
- official_types = possible_types . reject { |type | type . content_type . match ( /\/ x-/ ) }
49
- ( types_matching_file . first || official_types . first || possible_types . first ) . content_type
66
+
67
+ def best_from_possible_types
68
+ @best_from_possible_types ||= ( types_matching_file . first || official_types . first || possible_types . first )
50
69
end
51
70
52
71
def type_from_file_command
0 commit comments