file_validators 2.1.0 → 3.0.0
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 +2 -0
- data/.rubocop.yml +32 -0
- data/.tool-versions +1 -0
- data/.travis.yml +43 -7
- data/Appraisals +13 -13
- data/CHANGELOG.md +31 -0
- data/Gemfile +2 -0
- data/README.md +26 -18
- 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.2.gemfile → activemodel_5.0.gemfile} +1 -1
- data/gemfiles/{activemodel_4.1.gemfile → activemodel_6.0.gemfile} +1 -1
- data/gemfiles/{activemodel_3.0.gemfile → activemodel_6.1.gemfile} +1 -1
- data/lib/file_validators.rb +6 -7
- data/lib/file_validators/error.rb +6 -0
- data/lib/file_validators/mime_type_analyzer.rb +106 -0
- data/lib/file_validators/validators/file_content_type_validator.rb +37 -33
- 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 +90 -32
- data/spec/lib/file_validators/validators/file_size_validator_spec.rb +84 -30
- data/spec/spec_helper.rb +5 -0
- 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 +86 -28
- data/CHANGELOG.mod +0 -6
- data/gemfiles/activemodel_3.1.gemfile +0 -8
- data/lib/file_validators/utils/content_type_detector.rb +0 -64
- 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: 3a55a08bc74303b69e964f61e3726e8ee00278c4cd0cd6fc217691c85114bcfb
|
4
|
+
data.tar.gz: be3b6d628de09d262a97abf3099ff66fd2e89dd27eec3030523a7f36c82e17df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf5b75fb78eaf84f42abef75255572b1917012fbdbdd70d771da14c366619677fb69f4c45d688b8851d5b99dbba44ccd08da6c7025c3819c40259e23502f5c61
|
7
|
+
data.tar.gz: 92e9c49f94d8cbc1c2f8aa7425527e84555e94d25c12b6ca1b51e4d5e51f765defb274264996174e6757d8606270f5513232be64d234717075abab8680d8f7c9
|
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,19 +1,55 @@
|
|
1
1
|
language: ruby
|
2
2
|
|
3
3
|
rvm:
|
4
|
-
- 2.0
|
5
4
|
- 2.2.3
|
5
|
+
- 2.5.0
|
6
|
+
- 3.0.0
|
6
7
|
- ruby-head
|
7
|
-
- jruby-9.
|
8
|
+
- jruby-9.1.7.0
|
9
|
+
- jruby-9.2.13.0
|
8
10
|
|
9
11
|
gemfile:
|
10
|
-
- gemfiles/
|
11
|
-
- gemfiles/
|
12
|
+
- gemfiles/activemodel_6.1.gemfile
|
13
|
+
- gemfiles/activemodel_6.0.gemfile
|
14
|
+
- gemfiles/activemodel_5.0.gemfile
|
12
15
|
- gemfiles/activemodel_4.0.gemfile
|
13
16
|
- gemfiles/activemodel_3.2.gemfile
|
14
|
-
- gemfiles/activemodel_3.1.gemfile
|
15
|
-
- gemfiles/activemodel_3.0.gemfile
|
16
17
|
|
17
18
|
matrix:
|
19
|
+
exclude:
|
20
|
+
- rvm: 2.2.3
|
21
|
+
gemfile: gemfiles/activemodel_6.0.gemfile
|
22
|
+
- rvm: 2.2.3
|
23
|
+
gemfile: gemfiles/activemodel_6.1.gemfile
|
24
|
+
|
25
|
+
- rvm: 2.5.0
|
26
|
+
gemfile: gemfiles/activemodel_3.2.gemfile
|
27
|
+
- rvm: 2.5.0
|
28
|
+
gemfile: gemfiles/activemodel_4.0.gemfile
|
29
|
+
|
30
|
+
- rvm: 3.0.0
|
31
|
+
gemfile: gemfiles/activemodel_3.2.gemfile
|
32
|
+
- rvm: 3.0.0
|
33
|
+
gemfile: gemfiles/activemodel_4.0.gemfile
|
34
|
+
- rvm: 3.0.0
|
35
|
+
gemfile: gemfiles/activemodel_5.0.gemfile
|
36
|
+
|
37
|
+
- rvm: ruby-head
|
38
|
+
gemfile: gemfiles/activemodel_3.2.gemfile
|
39
|
+
- rvm: ruby-head
|
40
|
+
gemfile: gemfiles/activemodel_4.0.gemfile
|
41
|
+
- rvm: ruby-head
|
42
|
+
gemfile: gemfiles/activemodel_5.0.gemfile
|
43
|
+
|
44
|
+
- rvm: jruby-9.1.7.0
|
45
|
+
gemfile: gemfiles/activemodel_6.0.gemfile
|
46
|
+
- rvm: jruby-9.1.7.0
|
47
|
+
gemfile: gemfiles/activemodel_6.1.gemfile
|
48
|
+
|
49
|
+
- rvm: jruby-9.2.13.0
|
50
|
+
gemfile: gemfiles/activemodel_3.2.gemfile
|
51
|
+
- rvm: jruby-9.2.13.0
|
52
|
+
gemfile: gemfiles/activemodel_4.0.gemfile
|
53
|
+
|
18
54
|
allow_failures:
|
19
|
-
|
55
|
+
- rvm: ruby-head
|
data/Appraisals
CHANGED
@@ -1,23 +1,23 @@
|
|
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'
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise 'activemodel-5.0' do
|
14
|
+
gem 'activemodel', '5.0.1'
|
15
15
|
end
|
16
16
|
|
17
|
-
appraise 'activemodel-
|
18
|
-
gem 'activemodel', '
|
17
|
+
appraise 'activemodel-6.0' do
|
18
|
+
gem 'activemodel', '6.0.3'
|
19
19
|
end
|
20
20
|
|
21
|
-
appraise 'activemodel-
|
22
|
-
gem 'activemodel', '
|
21
|
+
appraise 'activemodel-6.1' do
|
22
|
+
gem 'activemodel', '6.1.0'
|
23
23
|
end
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# 3.0.0
|
2
|
+
|
3
|
+
* [#32](https://github.com/musaffa/file_validators/pull/32) Removed cocaine/terrapin. Added options for choosing MIME type analyzers with `:tool` option.
|
4
|
+
* [#40](https://github.com/musaffa/file_validators/pull/40) Added Support for Ruby 3.
|
5
|
+
* Rubocop style guide
|
6
|
+
|
7
|
+
# 3.0.0.beta2
|
8
|
+
|
9
|
+
* [#32](https://github.com/musaffa/file_validators/pull/32) Removed terrapin. Added options for choosing MIME type analyzers with `:tool` option.
|
10
|
+
|
11
|
+
# 3.0.0.beta1
|
12
|
+
|
13
|
+
* [#29](https://github.com/musaffa/file_validators/pull/29) Upgrade cocaine to terrapin
|
14
|
+
* Rubocop style guide
|
15
|
+
|
16
|
+
# 2.3.0
|
17
|
+
|
18
|
+
* [#19](https://github.com/musaffa/file_validators/pull/19) Return false with blank size
|
19
|
+
* [#27](https://github.com/musaffa/file_validators/pull/27) Fix file size validator for ActiveStorage
|
20
|
+
|
21
|
+
# 2.2.0-beta.1
|
22
|
+
|
23
|
+
* [#17](https://github.com/musaffa/file_validators/pull/17) Now Supports multiple file uploads
|
24
|
+
* 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.
|
25
|
+
|
26
|
+
# 2.1.0
|
27
|
+
|
28
|
+
* Use autoload for lazy loading of libraries.
|
29
|
+
* Media type spoof valiation is moved to content type detector.
|
30
|
+
* `spoofed_file_media_type` message isn't needed anymore.
|
31
|
+
* Logger info and warning is added.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/file_validators)
|
4
4
|
[](https://travis-ci.org/musaffa/file_validators)
|
5
|
-
[](https://gemnasium.com/musaffa/file_validators)
|
6
5
|
[](https://coveralls.io/r/musaffa/file_validators)
|
7
6
|
[](https://codeclimate.com/github/musaffa/file_validators)
|
8
7
|
[](http://inch-ci.org/github/musaffa/file_validators)
|
@@ -12,8 +11,11 @@ Any module that uses ActiveModel, for example ActiveRecord, can use these file v
|
|
12
11
|
|
13
12
|
## Support
|
14
13
|
|
15
|
-
* ActiveModel versions: 3 and
|
16
|
-
* Rails versions: 3 and
|
14
|
+
* ActiveModel versions: 3.2, 4, 5 and 6.
|
15
|
+
* Rails versions: 3.2, 4, 5 and 6.
|
16
|
+
|
17
|
+
As of version `2.2`, activemodel 3.0 and 3.1 will no longer be supported.
|
18
|
+
For activemodel 3.0 and 3.1, please use file_validators version `<= 2.1`.
|
17
19
|
|
18
20
|
It has been tested to work with Carrierwave, Paperclip, Dragonfly, Refile etc file uploading solutions.
|
19
21
|
Validations works both before and after uploads.
|
@@ -36,7 +38,7 @@ class Profile
|
|
36
38
|
|
37
39
|
attr_accessor :avatar
|
38
40
|
validates :avatar, file_size: { less_than_or_equal_to: 100.kilobytes },
|
39
|
-
file_content_type: { allow: ['image/jpeg', 'image/png'] }
|
41
|
+
file_content_type: { allow: ['image/jpeg', 'image/png'] }
|
40
42
|
end
|
41
43
|
```
|
42
44
|
ActiveRecord example:
|
@@ -64,23 +66,23 @@ validates :avatar, file_size: { less_than: 2.gigabytes }
|
|
64
66
|
```
|
65
67
|
* `less_than_or_equal_to`: Less than or equal to a number in bytes or a proc that returns a number
|
66
68
|
```ruby
|
67
|
-
validates :avatar, file_size: { less_than_or_equal_to: 50.bytes }
|
69
|
+
validates :avatar, file_size: { less_than_or_equal_to: 50.bytes }
|
68
70
|
```
|
69
71
|
* `greater_than`: greater than a number in bytes or a proc that returns a number
|
70
72
|
```ruby
|
71
|
-
validates :avatar, file_size: { greater_than: 1.byte }
|
73
|
+
validates :avatar, file_size: { greater_than: 1.byte }
|
72
74
|
```
|
73
75
|
* `greater_than_or_equal_to`: Greater than or equal to a number in bytes or a proc that returns a number
|
74
76
|
```ruby
|
75
|
-
validates :avatar, file_size: { greater_than_or_equal_to: 50.bytes }
|
77
|
+
validates :avatar, file_size: { greater_than_or_equal_to: 50.bytes }
|
76
78
|
```
|
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.
|
79
|
+
* `message`: Error message to display. With all the options above except `:in`, you will get `count` as a replacement.
|
80
|
+
With `:in` you will get `min` and `max` as replacements.
|
79
81
|
`count`, `min` and `max` each will have its value and unit together.
|
80
82
|
You can write error messages without using any replacement.
|
81
83
|
```ruby
|
82
84
|
validates :avatar, file_size: { less_than: 100.kilobytes,
|
83
|
-
message: 'avatar should be less than %{count}' }
|
85
|
+
message: 'avatar should be less than %{count}' }
|
84
86
|
```
|
85
87
|
```ruby
|
86
88
|
validates :document, file_size: { in: 1.kilobyte..1.megabyte,
|
@@ -139,8 +141,13 @@ validates :video, file_content_type: { allow: lambda { |record| record.content_t
|
|
139
141
|
can be a String or a Regexp. It also accepts `proc`. See `:allow` options examples.
|
140
142
|
* `mode`: `:strict` or `:relaxed`. `:strict` mode can detect content type based on the contents
|
141
143
|
of the files. It also detects media type spoofing (see more in [security](#security)).
|
142
|
-
`:relaxed` mode uses file name to detect
|
143
|
-
|
144
|
+
`:file` analyzer is used in `:strict` mode. `:relaxed` mode uses file name to detect
|
145
|
+
the content type. `mime_types` analyzer is used in `relaxed` mode. If mode option is not
|
146
|
+
set then the validator uses form supplied content type.
|
147
|
+
* `tool`: `:file`, `:fastimage`, `:filemagic`, `:mimemagic`, `:marcel`, `:mime_types`, `:mini_mime`.
|
148
|
+
You can choose one of these built-in MIME type analyzers. You have to install the analyzer gem you choose.
|
149
|
+
By default supplied content type is used to determine the MIME type. This option takes precedence
|
150
|
+
over `mode` option.
|
144
151
|
```ruby
|
145
152
|
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :strict }
|
146
153
|
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :relaxed }
|
@@ -169,7 +176,7 @@ validates :avatar, file_content_type: { allow: /^image\/.*/, exclude: ['image/pn
|
|
169
176
|
This gem can use Unix file command to get the content type based on the content of the file rather
|
170
177
|
than the extension. This prevents fake content types inserted in the request header.
|
171
178
|
|
172
|
-
It also prevents file media type spoofing. For example, user may upload a .html document as
|
179
|
+
It also prevents file media type spoofing. For example, user may upload a .html document as
|
173
180
|
a part of the EXIF header of a valid JPEG file. Content type validator will identify its content type
|
174
181
|
as `image/jpeg` and, without spoof detection, it may pass the validation and be saved as .html document
|
175
182
|
thus exposing your application to a security vulnerability. Media type spoof detector wont let that happen.
|
@@ -177,8 +184,8 @@ It will not allow a file having `image/jpeg` content type to be saved as `text/p
|
|
177
184
|
type mismatch, for example `text` of `text/plain` and `image` of `image/jpeg`. So it will not prevent
|
178
185
|
`image/jpeg` from saving as `image/png` as both have the same `image` media type.
|
179
186
|
|
180
|
-
**note**: This security feature is disabled by default. To enable it,
|
181
|
-
|
187
|
+
**note**: This security feature is disabled by default. To enable it, add `mode: :strict` option
|
188
|
+
in [content type validations](#file-content-type-validator).
|
182
189
|
`:strict` mode may not work in direct file uploading systems as the file is not passed along with the form.
|
183
190
|
|
184
191
|
## i18n Translations
|
@@ -198,7 +205,7 @@ of the file matches anyone of them. takes `types` as replacement.
|
|
198
205
|
|
199
206
|
This gem provides `en` translations for this errors under `errors.messages` namespace.
|
200
207
|
If you want to override and/or create other locales, you can
|
201
|
-
check [this](https://github.com/musaffa/file_validators/blob/master/lib/file_validators/locale/en.yml) out to see how translations are done.
|
208
|
+
check [this](https://github.com/musaffa/file_validators/blob/master/lib/file_validators/locale/en.yml) out to see how translations are done.
|
202
209
|
|
203
210
|
You can override all of them with the `:message` option.
|
204
211
|
|
@@ -251,10 +258,11 @@ uploaders start processing a file immediately after its assignment (even before
|
|
251
258
|
$ rake
|
252
259
|
$ rake test:unit
|
253
260
|
$ rake test:integration
|
261
|
+
$ rubocop
|
254
262
|
|
255
263
|
# test different active model versions
|
256
|
-
$ appraisal install
|
257
|
-
$ appraisal rake
|
264
|
+
$ bundle exec appraisal install
|
265
|
+
$ bundle exec appraisal rake
|
258
266
|
```
|
259
267
|
|
260
268
|
## 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,16 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_model'
|
2
4
|
require 'ostruct'
|
3
5
|
|
4
6
|
module FileValidators
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
autoload :ContentTypeDetector
|
9
|
-
autoload :MediaTypeSpoofDetector
|
10
|
-
end
|
7
|
+
extend ActiveSupport::Autoload
|
8
|
+
autoload :Error
|
9
|
+
autoload :MimeTypeAnalyzer
|
11
10
|
end
|
12
11
|
|
13
|
-
Dir[File.dirname(__FILE__) +
|
12
|
+
Dir[File.dirname(__FILE__) + '/file_validators/validators/*.rb'].each { |file| require file }
|
14
13
|
|
15
14
|
locale_path = Dir.glob(File.dirname(__FILE__) + '/file_validators/locale/*.yml')
|
16
15
|
I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
|
@@ -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
|