active_storage_validations 1.3.4 → 1.4.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 +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
|