active_storage_validations 1.3.4 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +5 -67
- data/config/locales/da.yml +1 -0
- data/config/locales/de.yml +1 -0
- data/config/locales/en.yml +1 -0
- data/config/locales/es.yml +1 -0
- data/config/locales/fr.yml +1 -0
- data/config/locales/it.yml +1 -0
- data/config/locales/ja.yml +1 -0
- data/config/locales/nl.yml +1 -0
- data/config/locales/pl.yml +1 -0
- data/config/locales/pt-BR.yml +1 -0
- data/config/locales/ru.yml +1 -0
- data/config/locales/sv.yml +1 -0
- data/config/locales/tr.yml +1 -0
- data/config/locales/uk.yml +1 -0
- data/config/locales/vi.yml +1 -0
- data/config/locales/zh-CN.yml +1 -0
- data/lib/active_storage_validations/analyzer/image_analyzer/image_magick.rb +47 -0
- data/lib/active_storage_validations/analyzer/image_analyzer/vips.rb +58 -0
- data/lib/active_storage_validations/analyzer/image_analyzer.rb +93 -0
- data/lib/active_storage_validations/analyzer/null_analyzer.rb +18 -0
- data/lib/active_storage_validations/analyzer.rb +34 -0
- data/lib/active_storage_validations/aspect_ratio_validator.rb +150 -118
- data/lib/active_storage_validations/content_type_spoof_detector.rb +3 -1
- data/lib/active_storage_validations/content_type_validator.rb +13 -5
- data/lib/active_storage_validations/dimension_validator.rb +2 -0
- data/lib/active_storage_validations/marcel_extensor.rb +2 -0
- data/lib/active_storage_validations/matchers/processable_image_validator_matcher.rb +4 -4
- data/lib/active_storage_validations/matchers.rb +2 -1
- data/lib/active_storage_validations/processable_image_validator.rb +2 -0
- data/lib/active_storage_validations/shared/asv_active_storageable.rb +2 -2
- data/lib/active_storage_validations/shared/asv_analyzable.rb +45 -0
- data/lib/active_storage_validations/shared/asv_attachable.rb +63 -16
- data/lib/active_storage_validations/version.rb +1 -1
- data/lib/active_storage_validations.rb +6 -0
- metadata +8 -3
- data/lib/active_storage_validations/metadata.rb +0 -179
@@ -1,179 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'shared/asv_loggable'
|
4
|
-
|
5
|
-
module ActiveStorageValidations
|
6
|
-
class Metadata
|
7
|
-
include ASVLoggable
|
8
|
-
|
9
|
-
class InvalidImageError < StandardError; end
|
10
|
-
|
11
|
-
attr_reader :attachable
|
12
|
-
|
13
|
-
DEFAULT_IMAGE_PROCESSOR = :mini_magick.freeze
|
14
|
-
|
15
|
-
def initialize(attachable)
|
16
|
-
require_image_processor
|
17
|
-
@attachable = attachable
|
18
|
-
end
|
19
|
-
|
20
|
-
def valid?
|
21
|
-
read_image
|
22
|
-
true
|
23
|
-
rescue InvalidImageError
|
24
|
-
false
|
25
|
-
end
|
26
|
-
|
27
|
-
def metadata
|
28
|
-
read_image do |image|
|
29
|
-
if rotated_image?(image)
|
30
|
-
{ width: image.height, height: image.width }
|
31
|
-
else
|
32
|
-
{ width: image.width, height: image.height }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
rescue InvalidImageError
|
36
|
-
logger.info "Skipping image analysis because ImageMagick or Vips doesn't support the file"
|
37
|
-
{}
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def image_processor
|
43
|
-
# Rails returns nil for default image processor, because it is set in an after initialize callback
|
44
|
-
# https://github.com/rails/rails/blob/89d8569abe2564c8187debf32dd3b4e33d6ad983/activestorage/lib/active_storage/engine.rb
|
45
|
-
Rails.application.config.active_storage.variant_processor || DEFAULT_IMAGE_PROCESSOR
|
46
|
-
end
|
47
|
-
|
48
|
-
def require_image_processor
|
49
|
-
case image_processor
|
50
|
-
when :vips then require 'vips' unless defined?(Vips)
|
51
|
-
when :mini_magick then require 'mini_magick' unless defined?(MiniMagick)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def exception_class
|
56
|
-
case image_processor
|
57
|
-
when :vips then Vips::Error
|
58
|
-
when :mini_magick then MiniMagick::Error
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def vips_image_processor?
|
63
|
-
image_processor == :vips
|
64
|
-
end
|
65
|
-
|
66
|
-
def read_image
|
67
|
-
is_string = attachable.is_a?(String)
|
68
|
-
if is_string || attachable.is_a?(ActiveStorage::Blob)
|
69
|
-
blob = is_string ? ActiveStorage::Blob.find_signed!(attachable) : attachable
|
70
|
-
|
71
|
-
tempfile = Tempfile.new(["ActiveStorage-#{blob.id}-", blob.filename.extension_with_delimiter])
|
72
|
-
tempfile.binmode
|
73
|
-
|
74
|
-
blob.download do |chunk|
|
75
|
-
tempfile.write(chunk)
|
76
|
-
end
|
77
|
-
|
78
|
-
tempfile.flush
|
79
|
-
tempfile.rewind
|
80
|
-
|
81
|
-
image = new_image_from_path(tempfile.path)
|
82
|
-
else
|
83
|
-
file_path = read_file_path
|
84
|
-
image = new_image_from_path(file_path)
|
85
|
-
end
|
86
|
-
|
87
|
-
|
88
|
-
raise InvalidImageError unless valid_image?(image)
|
89
|
-
yield image if block_given?
|
90
|
-
rescue LoadError, NameError
|
91
|
-
logger.info "Skipping image analysis because the mini_magick or ruby-vips gem isn't installed"
|
92
|
-
{}
|
93
|
-
rescue exception_class => error
|
94
|
-
logger.error "Skipping image analysis due to an #{exception_class.name.split('::').map(&:downcase).join(' ').capitalize} error: #{error.message}"
|
95
|
-
{}
|
96
|
-
ensure
|
97
|
-
image = nil
|
98
|
-
end
|
99
|
-
|
100
|
-
def new_image_from_path(path)
|
101
|
-
if vips_image_processor? && (supported_vips_suffix?(path) || vips_version_below_8_8? || open_uri_tempfile?(path))
|
102
|
-
begin
|
103
|
-
Vips::Image.new_from_file(path)
|
104
|
-
rescue exception_class
|
105
|
-
# We handle cases where an error is raised when reading the attachable
|
106
|
-
# because Vips can throw errors rather than returning false
|
107
|
-
# We stumble upon this issue while reading 0 byte size attachable
|
108
|
-
# https://github.com/janko/image_processing/issues/97
|
109
|
-
false
|
110
|
-
end
|
111
|
-
elsif defined?(MiniMagick)
|
112
|
-
MiniMagick::Image.new(path)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def supported_vips_suffix?(path)
|
117
|
-
Vips::get_suffixes.include?(File.extname(path).downcase)
|
118
|
-
end
|
119
|
-
|
120
|
-
def vips_version_below_8_8?
|
121
|
-
# FYI, Vips 8.8 was released in 2019
|
122
|
-
# https://github.com/libvips/libvips/releases/tag/v8.8.0
|
123
|
-
!Vips::respond_to?(:vips_foreign_get_suffixes)
|
124
|
-
end
|
125
|
-
|
126
|
-
def open_uri_tempfile?(path)
|
127
|
-
# When trying to open urls for 'large' images, OpenURI will return a
|
128
|
-
# tempfile. That tempfile does not have an extension indicating the type
|
129
|
-
# of file. However, Vips will be able to process it anyway.
|
130
|
-
# The 'large' file value is derived from OpenUri::Buffer class (> 10ko)
|
131
|
-
path.split('/').last.starts_with?("open-uri")
|
132
|
-
end
|
133
|
-
|
134
|
-
def valid_image?(image)
|
135
|
-
return false unless image
|
136
|
-
|
137
|
-
vips_image_processor? && image.is_a?(Vips::Image) ? image.avg : image.valid?
|
138
|
-
rescue exception_class
|
139
|
-
false
|
140
|
-
end
|
141
|
-
|
142
|
-
def rotated_image?(image)
|
143
|
-
if vips_image_processor? && image.is_a?(Vips::Image)
|
144
|
-
image.get('exif-ifd0-Orientation').include?('Right-top') ||
|
145
|
-
image.get('exif-ifd0-Orientation').include?('Left-bottom')
|
146
|
-
else
|
147
|
-
%w[ RightTop LeftBottom ].include?(image["%[orientation]"])
|
148
|
-
end
|
149
|
-
rescue exception_class # field "exif-ifd0-Orientation" not found
|
150
|
-
false
|
151
|
-
end
|
152
|
-
|
153
|
-
def read_file_path
|
154
|
-
case attachable
|
155
|
-
when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
|
156
|
-
attachable.path
|
157
|
-
when Hash
|
158
|
-
io = attachable.fetch(:io)
|
159
|
-
if io.is_a?(StringIO)
|
160
|
-
tempfile = Tempfile.new([File.basename(attachable[:filename], '.*'), File.extname(attachable[:filename])])
|
161
|
-
tempfile.binmode
|
162
|
-
IO.copy_stream(io, tempfile)
|
163
|
-
io.rewind
|
164
|
-
tempfile.flush
|
165
|
-
tempfile.rewind
|
166
|
-
tempfile.path
|
167
|
-
else
|
168
|
-
File.open(io).path
|
169
|
-
end
|
170
|
-
when File
|
171
|
-
attachable.path
|
172
|
-
when Pathname
|
173
|
-
attachable.to_s
|
174
|
-
else
|
175
|
-
raise "Something wrong with params."
|
176
|
-
end
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|