file_validators 2.0.2 → 3.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.gitignore +3 -0
- data/.rubocop.yml +32 -0
- data/.tool-versions +1 -0
- data/.travis.yml +33 -5
- data/Appraisals +16 -10
- data/CHANGELOG.md +26 -0
- data/Gemfile +2 -0
- data/README.md +32 -21
- data/Rakefile +3 -1
- data/file_validators.gemspec +13 -7
- data/gemfiles/activemodel_3.2.gemfile +2 -1
- data/gemfiles/activemodel_4.0.gemfile +2 -1
- data/gemfiles/activemodel_4.1.gemfile +1 -0
- data/gemfiles/activemodel_4.2.gemfile +2 -1
- data/gemfiles/{activemodel_3.0.gemfile → activemodel_5.0.gemfile} +1 -1
- data/gemfiles/{activemodel_3.1.gemfile → activemodel_5.2.gemfile} +1 -1
- data/lib/file_validators.rb +11 -2
- data/lib/file_validators/error.rb +6 -0
- data/lib/file_validators/locale/en.yml +0 -2
- data/lib/file_validators/mime_type_analyzer.rb +106 -0
- data/lib/file_validators/validators/file_content_type_validator.rb +37 -42
- data/lib/file_validators/validators/file_size_validator.rb +62 -19
- data/lib/file_validators/version.rb +3 -1
- data/spec/integration/combined_validators_integration_spec.rb +3 -1
- data/spec/integration/file_content_type_validation_integration_spec.rb +117 -11
- data/spec/integration/file_size_validator_integration_spec.rb +100 -10
- data/spec/lib/file_validators/mime_type_analyzer_spec.rb +139 -0
- data/spec/lib/file_validators/validators/file_content_type_validator_spec.rb +93 -36
- data/spec/lib/file_validators/validators/file_size_validator_spec.rb +87 -34
- data/spec/spec_helper.rb +9 -1
- data/spec/support/fakeio.rb +17 -0
- data/spec/support/helpers.rb +7 -0
- data/spec/support/matchers/allow_content_type.rb +2 -0
- data/spec/support/matchers/allow_file_size.rb +2 -0
- metadata +87 -27
- data/lib/file_validators/utils/content_type_detector.rb +0 -46
- data/lib/file_validators/utils/media_type_spoof_detector.rb +0 -46
- data/spec/lib/file_validators/utils/content_type_detector_spec.rb +0 -27
- data/spec/lib/file_validators/utils/media_type_spoof_detector_spec.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7536b453f528e82937d43e4349c6cb99f990832023a5567d125b817b59f3b71a
|
4
|
+
data.tar.gz: e33270b079d15c08aed6a2b3e1f2aab9f61d39fc222f9c42e15a0a9a34f82885
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c47e7fe802dc65553c2dfa5a72c2d05d97d4d14dcdf2c6826f7ae18e586a8754b1b4dc07c20abec2038b55c6d1766df96c19f968217630270586c92bab4101f
|
7
|
+
data.tar.gz: b8b0c693d4314614c2cd8f72a699b48c666487c4b44221f25daf9f70cc0d27d1b09766b9ef8e035ed14690ea9d24aac10cdbcc62d1e89bf2a683e46a7e39fec0
|
data/.gitignore
CHANGED
data/.rubocop.yml
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
Bundler/OrderedGems:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Style/Documentation:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Style/MissingRespondToMissing:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Style/CaseEquality:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
Style/GuardClause:
|
14
|
+
Enabled: false
|
15
|
+
|
16
|
+
Style/RegexpLiteral:
|
17
|
+
Enabled: false
|
18
|
+
|
19
|
+
Style/Next:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
Metrics/LineLength:
|
23
|
+
Max: 110
|
24
|
+
|
25
|
+
Metrics/ModuleLength:
|
26
|
+
Enabled: false
|
27
|
+
|
28
|
+
Metrics/BlockLength:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
Metrics/MethodLength:
|
32
|
+
Enabled: false
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 2.3.1
|
data/.travis.yml
CHANGED
@@ -1,11 +1,39 @@
|
|
1
1
|
language: ruby
|
2
|
+
|
2
3
|
rvm:
|
3
|
-
-
|
4
|
-
- 2.
|
4
|
+
- 2.2.3
|
5
|
+
- 2.5.0
|
6
|
+
- ruby-head
|
7
|
+
- jruby-9.0.4.0
|
8
|
+
- jruby-9.1.7.0
|
9
|
+
|
5
10
|
gemfile:
|
11
|
+
- gemfiles/activemodel_5.2.gemfile
|
12
|
+
- gemfiles/activemodel_5.0.gemfile
|
6
13
|
- gemfiles/activemodel_4.2.gemfile
|
7
|
-
- gemfiles/activemodel_4.1.gemfile
|
8
14
|
- gemfiles/activemodel_4.0.gemfile
|
9
15
|
- gemfiles/activemodel_3.2.gemfile
|
10
|
-
|
11
|
-
|
16
|
+
|
17
|
+
matrix:
|
18
|
+
exclude:
|
19
|
+
- rvm: 2.5.0
|
20
|
+
gemfile: gemfiles/activemodel_3.2.gemfile
|
21
|
+
- rvm: 2.5.0
|
22
|
+
gemfile: gemfiles/activemodel_4.0.gemfile
|
23
|
+
- rvm: 2.5.0
|
24
|
+
gemfile: gemfiles/activemodel_4.2.gemfile
|
25
|
+
|
26
|
+
- rvm: ruby-head
|
27
|
+
gemfile: gemfiles/activemodel_3.2.gemfile
|
28
|
+
- rvm: ruby-head
|
29
|
+
gemfile: gemfiles/activemodel_4.0.gemfile
|
30
|
+
- rvm: ruby-head
|
31
|
+
gemfile: gemfiles/activemodel_4.2.gemfile
|
32
|
+
|
33
|
+
- rvm: jruby-9.0.4.0
|
34
|
+
gemfile: gemfiles/activemodel_5.0.gemfile
|
35
|
+
- rvm: jruby-9.0.4.0
|
36
|
+
gemfile: gemfiles/activemodel_5.2.gemfile
|
37
|
+
|
38
|
+
allow_failures:
|
39
|
+
- rvm: ruby-head
|
data/Appraisals
CHANGED
@@ -1,23 +1,29 @@
|
|
1
|
-
|
2
|
-
gem 'activemodel', '3.0.20'
|
3
|
-
end
|
4
|
-
|
5
|
-
appraise 'activemodel-3.1' do
|
6
|
-
gem 'activemodel', '3.1.12'
|
7
|
-
end
|
1
|
+
# frozen_string_literal: true
|
8
2
|
|
9
3
|
appraise 'activemodel-3.2' do
|
10
|
-
gem 'activemodel', '3.2.
|
4
|
+
gem 'activemodel', '3.2.22.5'
|
5
|
+
gem 'rack', '1.6.5'
|
11
6
|
end
|
12
7
|
|
13
8
|
appraise 'activemodel-4.0' do
|
14
|
-
gem 'activemodel', '4.0.
|
9
|
+
gem 'activemodel', '4.0.13'
|
10
|
+
gem 'rack', '1.6.5'
|
15
11
|
end
|
16
12
|
|
17
13
|
appraise 'activemodel-4.1' do
|
18
14
|
gem 'activemodel', '4.1.6'
|
15
|
+
gem 'rack', '1.6.5'
|
19
16
|
end
|
20
17
|
|
21
18
|
appraise 'activemodel-4.2' do
|
22
|
-
gem 'activemodel', '4.2.
|
19
|
+
gem 'activemodel', '4.2.7.1'
|
20
|
+
gem 'rack', '1.6.5'
|
21
|
+
end
|
22
|
+
|
23
|
+
appraise 'activemodel-5.0' do
|
24
|
+
gem 'activemodel', '5.0.1'
|
25
|
+
end
|
26
|
+
|
27
|
+
appraise 'activemodel-5.2' do
|
28
|
+
gem 'activemodel', '5.2.1'
|
23
29
|
end
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# 3.0.0.beta2
|
2
|
+
|
3
|
+
* [#32](https://github.com/musaffa/file_validators/pull/32) Removed terrapin. Added options for choosing MIME type analyzers with `:tool` option.
|
4
|
+
* Rubocop style guide
|
5
|
+
|
6
|
+
# 3.0.0.beta1
|
7
|
+
|
8
|
+
* [#29](https://github.com/musaffa/file_validators/pull/29) Upgrade cocaine to terrapin
|
9
|
+
* Rubocop style guide
|
10
|
+
|
11
|
+
# 2.3.0
|
12
|
+
|
13
|
+
* [#19](https://github.com/musaffa/file_validators/pull/19) Return false with blank size
|
14
|
+
* [#27](https://github.com/musaffa/file_validators/pull/27) Fix file size validator for ActiveStorage
|
15
|
+
|
16
|
+
# 2.2.0-beta.1
|
17
|
+
|
18
|
+
* [#17](https://github.com/musaffa/file_validators/pull/17) Now Supports multiple file uploads
|
19
|
+
* As activemodel 3.0 and 3.1 doesn't support `added?` method on the Errors class, the support for both of them have been deprecated in this release.
|
20
|
+
|
21
|
+
# 2.1.0
|
22
|
+
|
23
|
+
* Use autoload for lazy loading of libraries.
|
24
|
+
* Media type spoof valiation is moved to content type detector.
|
25
|
+
* `spoofed_file_media_type` message isn't needed anymore.
|
26
|
+
* Logger info and warning is added.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -12,8 +12,11 @@ Any module that uses ActiveModel, for example ActiveRecord, can use these file v
|
|
12
12
|
|
13
13
|
## Support
|
14
14
|
|
15
|
-
* ActiveModel versions: 3 and
|
16
|
-
* Rails versions: 3 and
|
15
|
+
* ActiveModel versions: 3.2, 4 and 5.
|
16
|
+
* Rails versions: 3.2, 4 and 5.
|
17
|
+
|
18
|
+
As of version `2.2`, activemodel 3.0 and 3.1 will no longer be supported.
|
19
|
+
For activemodel 3.0 and 3.1, please use file_validators version `<= 2.1`.
|
17
20
|
|
18
21
|
It has been tested to work with Carrierwave, Paperclip, Dragonfly, Refile etc file uploading solutions.
|
19
22
|
Validations works both before and after uploads.
|
@@ -36,7 +39,7 @@ class Profile
|
|
36
39
|
|
37
40
|
attr_accessor :avatar
|
38
41
|
validates :avatar, file_size: { less_than_or_equal_to: 100.kilobytes },
|
39
|
-
file_content_type: { allow: ['image/jpeg', 'image/png'] }
|
42
|
+
file_content_type: { allow: ['image/jpeg', 'image/png'] }
|
40
43
|
end
|
41
44
|
```
|
42
45
|
ActiveRecord example:
|
@@ -64,23 +67,23 @@ validates :avatar, file_size: { less_than: 2.gigabytes }
|
|
64
67
|
```
|
65
68
|
* `less_than_or_equal_to`: Less than or equal to a number in bytes or a proc that returns a number
|
66
69
|
```ruby
|
67
|
-
validates :avatar, file_size: { less_than_or_equal_to: 50.bytes }
|
70
|
+
validates :avatar, file_size: { less_than_or_equal_to: 50.bytes }
|
68
71
|
```
|
69
72
|
* `greater_than`: greater than a number in bytes or a proc that returns a number
|
70
73
|
```ruby
|
71
|
-
validates :avatar, file_size: { greater_than: 1.byte }
|
74
|
+
validates :avatar, file_size: { greater_than: 1.byte }
|
72
75
|
```
|
73
76
|
* `greater_than_or_equal_to`: Greater than or equal to a number in bytes or a proc that returns a number
|
74
77
|
```ruby
|
75
|
-
validates :avatar, file_size: { greater_than_or_equal_to: 50.bytes }
|
78
|
+
validates :avatar, file_size: { greater_than_or_equal_to: 50.bytes }
|
76
79
|
```
|
77
|
-
* `message`: Error message to display. With all the options above except `:in`, you will get `count` as a replacement.
|
78
|
-
With `:in` you will get `min` and `max` as replacements.
|
80
|
+
* `message`: Error message to display. With all the options above except `:in`, you will get `count` as a replacement.
|
81
|
+
With `:in` you will get `min` and `max` as replacements.
|
79
82
|
`count`, `min` and `max` each will have its value and unit together.
|
80
83
|
You can write error messages without using any replacement.
|
81
84
|
```ruby
|
82
85
|
validates :avatar, file_size: { less_than: 100.kilobytes,
|
83
|
-
message: 'avatar should be less than %{count}' }
|
86
|
+
message: 'avatar should be less than %{count}' }
|
84
87
|
```
|
85
88
|
```ruby
|
86
89
|
validates :document, file_size: { in: 1.kilobyte..1.megabyte,
|
@@ -139,8 +142,13 @@ validates :video, file_content_type: { allow: lambda { |record| record.content_t
|
|
139
142
|
can be a String or a Regexp. It also accepts `proc`. See `:allow` options examples.
|
140
143
|
* `mode`: `:strict` or `:relaxed`. `:strict` mode can detect content type based on the contents
|
141
144
|
of the files. It also detects media type spoofing (see more in [security](#security)).
|
142
|
-
`:relaxed` mode uses file name to detect
|
143
|
-
|
145
|
+
`:file` analyzer is used in `:strict` model. `:relaxed` mode uses file name to detect
|
146
|
+
the content type. `mime_types` analyzer is used in `relaxed` mode. If mode option is not
|
147
|
+
set then the validator uses form supplied content type.
|
148
|
+
* `tool`: `:file`, `:fastimage`, `:filemagic`, `:mimemagic`, `:marcel`, `:mime_types`, `:mini_mime`.
|
149
|
+
You can choose one of these built-in MIME type analyzers. You have to install the analyzer gem you choose.
|
150
|
+
By default supplied content type is used to determine the MIME type. This option takes precedence
|
151
|
+
over `mode` option.
|
144
152
|
```ruby
|
145
153
|
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :strict }
|
146
154
|
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :relaxed }
|
@@ -169,7 +177,7 @@ validates :avatar, file_content_type: { allow: /^image\/.*/, exclude: ['image/pn
|
|
169
177
|
This gem can use Unix file command to get the content type based on the content of the file rather
|
170
178
|
than the extension. This prevents fake content types inserted in the request header.
|
171
179
|
|
172
|
-
It also prevents file media type spoofing. For example, user may upload a .html document as
|
180
|
+
It also prevents file media type spoofing. For example, user may upload a .html document as
|
173
181
|
a part of the EXIF header of a valid JPEG file. Content type validator will identify its content type
|
174
182
|
as `image/jpeg` and, without spoof detection, it may pass the validation and be saved as .html document
|
175
183
|
thus exposing your application to a security vulnerability. Media type spoof detector wont let that happen.
|
@@ -177,8 +185,8 @@ It will not allow a file having `image/jpeg` content type to be saved as `text/p
|
|
177
185
|
type mismatch, for example `text` of `text/plain` and `image` of `image/jpeg`. So it will not prevent
|
178
186
|
`image/jpeg` from saving as `image/png` as both have the same `image` media type.
|
179
187
|
|
180
|
-
**note**: This security feature is disabled by default. To enable it,
|
181
|
-
|
188
|
+
**note**: This security feature is disabled by default. To enable it, add `mode: :strict` option
|
189
|
+
in [content type validations](#file-content-type-validator).
|
182
190
|
`:strict` mode may not work in direct file uploading systems as the file is not passed along with the form.
|
183
191
|
|
184
192
|
## i18n Translations
|
@@ -191,8 +199,6 @@ File Size Errors
|
|
191
199
|
* `file_size_is_greater_than_or_equal_to`: takes `count` as replacement
|
192
200
|
|
193
201
|
Content Type Errors
|
194
|
-
* `spoofed_file_media_type`: generated when file media type from its extension doesn't match the media type of its
|
195
|
-
content. learn more from [security](#Security).
|
196
202
|
* `allowed_file_content_types`: generated when you have specified allowed types but the content type
|
197
203
|
of the file doesn't match. takes `types` as replacement.
|
198
204
|
* `excluded_file_content_types`: generated when you have specified excluded types and the content type
|
@@ -200,7 +206,7 @@ of the file matches anyone of them. takes `types` as replacement.
|
|
200
206
|
|
201
207
|
This gem provides `en` translations for this errors under `errors.messages` namespace.
|
202
208
|
If you want to override and/or create other locales, you can
|
203
|
-
check [this](https://github.com/musaffa/file_validators/blob/master/lib/file_validators/locale/en.yml) out to see how translations are done.
|
209
|
+
check [this](https://github.com/musaffa/file_validators/blob/master/lib/file_validators/locale/en.yml) out to see how translations are done.
|
204
210
|
|
205
211
|
You can override all of them with the `:message` option.
|
206
212
|
|
@@ -249,10 +255,15 @@ uploaders start processing a file immediately after its assignment (even before
|
|
249
255
|
|
250
256
|
## Tests
|
251
257
|
|
252
|
-
```
|
253
|
-
rake
|
254
|
-
rake test:unit
|
255
|
-
rake test:integration
|
258
|
+
```Shell
|
259
|
+
$ rake
|
260
|
+
$ rake test:unit
|
261
|
+
$ rake test:integration
|
262
|
+
$ rubocop
|
263
|
+
|
264
|
+
# test different active model versions
|
265
|
+
$ bundle exec appraisal install
|
266
|
+
$ bundle exec appraisal rake
|
256
267
|
```
|
257
268
|
|
258
269
|
## Problems
|
data/Rakefile
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
begin
|
2
4
|
require 'bundler/setup'
|
3
5
|
rescue LoadError
|
@@ -16,7 +18,7 @@ namespace :test do
|
|
16
18
|
end
|
17
19
|
end
|
18
20
|
|
19
|
-
task :
|
21
|
+
task default: ['test:unit', 'test:integration']
|
20
22
|
|
21
23
|
# require 'rdoc/task'
|
22
24
|
|
data/file_validators.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
2
4
|
|
3
5
|
require 'file_validators/version'
|
4
6
|
|
@@ -12,17 +14,21 @@ Gem::Specification.new do |s|
|
|
12
14
|
s.homepage = 'https://github.com/musaffa/file_validators'
|
13
15
|
s.license = 'MIT'
|
14
16
|
|
15
|
-
s.files = `git ls-files`.split(
|
17
|
+
s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
16
18
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
19
|
s.test_files = s.files.grep(%r{^spec/})
|
18
|
-
s.require_paths
|
20
|
+
s.require_paths = ['lib']
|
19
21
|
|
20
|
-
s.add_dependency 'activemodel', '>= 3.
|
22
|
+
s.add_dependency 'activemodel', '>= 3.2'
|
21
23
|
s.add_dependency 'mime-types', '>= 1.0'
|
22
24
|
|
23
|
-
s.add_development_dependency 'cocaine', '~> 0.5.4'
|
24
|
-
s.add_development_dependency 'rake'
|
25
|
-
s.add_development_dependency 'rspec', '~> 3.1.0'
|
26
25
|
s.add_development_dependency 'coveralls'
|
26
|
+
s.add_development_dependency 'fastimage'
|
27
|
+
s.add_development_dependency 'marcel', '~> 0.3' if RUBY_VERSION >= '2.2.0'
|
28
|
+
s.add_development_dependency 'mimemagic', '>= 0.3.2'
|
29
|
+
s.add_development_dependency 'mini_mime', '~> 1.0'
|
27
30
|
s.add_development_dependency 'rack-test'
|
31
|
+
s.add_development_dependency 'rake'
|
32
|
+
s.add_development_dependency 'rspec', '~> 3.5.0'
|
33
|
+
s.add_development_dependency 'rubocop', '~> 0.58.2'
|
28
34
|
end
|
data/lib/file_validators.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_model'
|
2
|
-
require '
|
3
|
-
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
module FileValidators
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
autoload :Error
|
9
|
+
autoload :MimeTypeAnalyzer
|
10
|
+
end
|
11
|
+
|
12
|
+
Dir[File.dirname(__FILE__) + '/file_validators/validators/*.rb'].each { |file| require file }
|
4
13
|
|
5
14
|
locale_path = Dir.glob(File.dirname(__FILE__) + '/file_validators/locale/*.yml')
|
6
15
|
I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
|
@@ -7,7 +7,5 @@ en:
|
|
7
7
|
file_size_is_greater_than: ! 'file size must be greater than %{count}'
|
8
8
|
file_size_is_greater_than_or_equal_to: ! 'file size must be greater than or equal to %{count}'
|
9
9
|
|
10
|
-
spoofed_file_media_type: file has an extension that does not match its contents
|
11
10
|
allowed_file_content_types: ! 'file should be one of %{types}'
|
12
11
|
excluded_file_content_types: ! 'file cannot be %{types}'
|
13
|
-
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Extracted from shrine/plugins/determine_mime_type.rb
|
4
|
+
module FileValidators
|
5
|
+
class MimeTypeAnalyzer
|
6
|
+
SUPPORTED_TOOLS = %i[fastimage file filemagic mimemagic marcel mime_types mini_mime].freeze
|
7
|
+
MAGIC_NUMBER = 256 * 1024
|
8
|
+
|
9
|
+
def initialize(tool)
|
10
|
+
raise Error, "unknown mime type analyzer #{tool.inspect}, supported analyzers are: #{SUPPORTED_TOOLS.join(',')}" unless SUPPORTED_TOOLS.include?(tool)
|
11
|
+
|
12
|
+
@tool = tool
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(io)
|
16
|
+
mime_type = send(:"extract_with_#{@tool}", io)
|
17
|
+
io.rewind
|
18
|
+
|
19
|
+
mime_type
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def extract_with_file(io)
|
25
|
+
require 'open3'
|
26
|
+
|
27
|
+
return nil if io.eof? # file command returns "application/x-empty" for empty files
|
28
|
+
|
29
|
+
Open3.popen3(*%W[file --mime-type --brief -]) do |stdin, stdout, stderr, thread|
|
30
|
+
begin
|
31
|
+
IO.copy_stream(io, stdin.binmode)
|
32
|
+
rescue Errno::EPIPE
|
33
|
+
end
|
34
|
+
stdin.close
|
35
|
+
|
36
|
+
status = thread.value
|
37
|
+
|
38
|
+
raise Error, "file command failed to spawn: #{stderr.read}" if status.nil?
|
39
|
+
raise Error, "file command failed: #{stderr.read}" unless status.success?
|
40
|
+
$stderr.print(stderr.read)
|
41
|
+
|
42
|
+
stdout.read.strip
|
43
|
+
end
|
44
|
+
rescue Errno::ENOENT
|
45
|
+
raise Error, 'file command-line tool is not installed'
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract_with_fastimage(io)
|
49
|
+
require 'fastimage'
|
50
|
+
|
51
|
+
type = FastImage.type(io)
|
52
|
+
"image/#{type}" if type
|
53
|
+
end
|
54
|
+
|
55
|
+
def extract_with_filemagic(io)
|
56
|
+
require 'filemagic'
|
57
|
+
|
58
|
+
return nil if io.eof? # FileMagic returns "application/x-empty" for empty files
|
59
|
+
|
60
|
+
FileMagic.open(FileMagic::MAGIC_MIME_TYPE) do |filemagic|
|
61
|
+
filemagic.buffer(io.read(MAGIC_NUMBER))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def extract_with_mimemagic(io)
|
66
|
+
require 'mimemagic'
|
67
|
+
|
68
|
+
mime = MimeMagic.by_magic(io)
|
69
|
+
mime.type if mime
|
70
|
+
end
|
71
|
+
|
72
|
+
def extract_with_marcel(io)
|
73
|
+
require 'marcel'
|
74
|
+
|
75
|
+
return nil if io.eof? # marcel returns "application/octet-stream" for empty files
|
76
|
+
|
77
|
+
Marcel::MimeType.for(io)
|
78
|
+
end
|
79
|
+
|
80
|
+
def extract_with_mime_types(io)
|
81
|
+
require 'mime/types'
|
82
|
+
|
83
|
+
if filename = extract_filename(io)
|
84
|
+
mime_type = MIME::Types.of(filename).first
|
85
|
+
mime_type.content_type if mime_type
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def extract_with_mini_mime(io)
|
90
|
+
require 'mini_mime'
|
91
|
+
|
92
|
+
if filename = extract_filename(io)
|
93
|
+
info = MiniMime.lookup_by_filename(filename)
|
94
|
+
info.content_type if info
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def extract_filename(io)
|
99
|
+
if io.respond_to?(:original_filename)
|
100
|
+
io.original_filename
|
101
|
+
elsif io.respond_to?(:path)
|
102
|
+
File.basename(io.path)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|