carrierwave 3.1.4 → 4.0.0.rc
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 +180 -3
- data/lib/carrierwave/direct_upload.rb +20 -0
- data/lib/carrierwave/downloader/base.rb +38 -9
- data/lib/carrierwave/downloader/remote_file.rb +3 -1
- data/lib/carrierwave/error.rb +1 -0
- data/lib/carrierwave/locale/en.yml +2 -0
- data/lib/carrierwave/mount.rb +10 -0
- data/lib/carrierwave/mounter.rb +79 -11
- data/lib/carrierwave/orm/activerecord.rb +51 -0
- data/lib/carrierwave/processing/mini_magick.rb +24 -13
- data/lib/carrierwave/processing/rmagick.rb +5 -0
- data/lib/carrierwave/processing/vips.rb +0 -24
- data/lib/carrierwave/sanitized_file.rb +2 -2
- data/lib/carrierwave/storage/abstract.rb +9 -0
- data/lib/carrierwave/storage/file.rb +2 -4
- data/lib/carrierwave/storage/fog.rb +98 -30
- data/lib/carrierwave/test/matchers.rb +24 -0
- data/lib/carrierwave/uploader/cache.rb +42 -9
- data/lib/carrierwave/uploader/configuration.rb +4 -0
- data/lib/carrierwave/uploader/direct_upload.rb +51 -0
- data/lib/carrierwave/uploader/metadata.rb +100 -0
- data/lib/carrierwave/uploader/processing.rb +18 -6
- data/lib/carrierwave/uploader/proxy.rb +18 -2
- data/lib/carrierwave/uploader/store.rb +48 -1
- data/lib/carrierwave/uploader/url.rb +3 -0
- data/lib/carrierwave/uploader/versions.rb +93 -50
- data/lib/carrierwave/uploader.rb +4 -0
- data/lib/carrierwave/utilities/uri.rb +32 -7
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +1 -0
- metadata +9 -6
|
@@ -6,6 +6,38 @@ module CarrierWave
|
|
|
6
6
|
|
|
7
7
|
include CarrierWave::Mount
|
|
8
8
|
|
|
9
|
+
##
|
|
10
|
+
# See +CarrierWave::Mount#mount_uploader+ for documentation
|
|
11
|
+
#
|
|
12
|
+
def mount_uploader(column, uploader=nil, options={}, &block)
|
|
13
|
+
super
|
|
14
|
+
|
|
15
|
+
mod = Module.new
|
|
16
|
+
prepend mod
|
|
17
|
+
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
|
|
18
|
+
def remote_#{column}_url=(url)
|
|
19
|
+
super
|
|
20
|
+
mark_#{column}_changed_on_error
|
|
21
|
+
end
|
|
22
|
+
RUBY
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
# See +CarrierWave::Mount#mount_uploaders+ for documentation
|
|
27
|
+
#
|
|
28
|
+
def mount_uploaders(column, uploader=nil, options={}, &block)
|
|
29
|
+
super
|
|
30
|
+
|
|
31
|
+
mod = Module.new
|
|
32
|
+
prepend mod
|
|
33
|
+
mod.class_eval <<-RUBY, __FILE__, __LINE__+1
|
|
34
|
+
def remote_#{column}_urls=(urls)
|
|
35
|
+
super
|
|
36
|
+
mark_#{column}_changed_on_error
|
|
37
|
+
end
|
|
38
|
+
RUBY
|
|
39
|
+
end
|
|
40
|
+
|
|
9
41
|
private
|
|
10
42
|
|
|
11
43
|
def mount_base(column, uploader=nil, options={}, &block)
|
|
@@ -52,6 +84,9 @@ module CarrierWave
|
|
|
52
84
|
@_mounters[:"#{column}"] = nil
|
|
53
85
|
# The attribute needs to be cleared to prevent it from picked up as identifier
|
|
54
86
|
write_uploader(_mounter(:#{column}).serialization_column, nil)
|
|
87
|
+
if (metadata_column = _mounter(:#{column}).metadata_column)
|
|
88
|
+
write_uploader(metadata_column, nil)
|
|
89
|
+
end
|
|
55
90
|
_mounter(:"#{column}").cache(old_uploaders)
|
|
56
91
|
end
|
|
57
92
|
|
|
@@ -59,6 +94,22 @@ module CarrierWave
|
|
|
59
94
|
return unless has_attribute?(_mounter(:#{column}).serialization_column)
|
|
60
95
|
super
|
|
61
96
|
end
|
|
97
|
+
|
|
98
|
+
def #{column}=(new_file)
|
|
99
|
+
super
|
|
100
|
+
mark_#{column}_changed_on_error
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
private
|
|
104
|
+
|
|
105
|
+
# A refusal leaves the column as it was, and a record saved through an
|
|
106
|
+
# association is only validated when it changed, so say it changed
|
|
107
|
+
def mark_#{column}_changed_on_error
|
|
108
|
+
return if #{column}_integrity_errors.empty? && #{column}_processing_errors.empty? &&
|
|
109
|
+
#{column}_download_errors.empty?
|
|
110
|
+
|
|
111
|
+
__send__(:"\#{_mounter(:#{column}).serialization_column}_will_change!")
|
|
112
|
+
end
|
|
62
113
|
RUBY
|
|
63
114
|
end
|
|
64
115
|
|
|
@@ -96,7 +96,7 @@ module CarrierWave
|
|
|
96
96
|
#
|
|
97
97
|
# === Yields
|
|
98
98
|
#
|
|
99
|
-
# [MiniMagick::Image] additional manipulations to perform
|
|
99
|
+
# [MiniMagick::Image] additional manipulations to perform (deprecated, use #manipulate!)
|
|
100
100
|
#
|
|
101
101
|
# === Examples
|
|
102
102
|
#
|
|
@@ -124,7 +124,7 @@ module CarrierWave
|
|
|
124
124
|
#
|
|
125
125
|
# === Yields
|
|
126
126
|
#
|
|
127
|
-
# [MiniMagick::Image] additional manipulations to perform
|
|
127
|
+
# [MiniMagick::Image] additional manipulations to perform (deprecated, use #manipulate!)
|
|
128
128
|
#
|
|
129
129
|
def resize_to_limit(width, height, combine_options: {}, &block)
|
|
130
130
|
width, height = resolve_dimensions(width, height)
|
|
@@ -148,7 +148,7 @@ module CarrierWave
|
|
|
148
148
|
#
|
|
149
149
|
# === Yields
|
|
150
150
|
#
|
|
151
|
-
# [MiniMagick::Image] additional manipulations to perform
|
|
151
|
+
# [MiniMagick::Image] additional manipulations to perform (deprecated, use #manipulate!)
|
|
152
152
|
#
|
|
153
153
|
def resize_to_fit(width, height, combine_options: {}, &block)
|
|
154
154
|
width, height = resolve_dimensions(width, height)
|
|
@@ -173,7 +173,7 @@ module CarrierWave
|
|
|
173
173
|
#
|
|
174
174
|
# === Yields
|
|
175
175
|
#
|
|
176
|
-
# [MiniMagick::Image] additional manipulations to perform
|
|
176
|
+
# [MiniMagick::Image] additional manipulations to perform (deprecated, use #manipulate!)
|
|
177
177
|
#
|
|
178
178
|
def resize_to_fill(width, height, gravity = 'Center', combine_options: {}, &block)
|
|
179
179
|
width, height = resolve_dimensions(width, height)
|
|
@@ -203,7 +203,7 @@ module CarrierWave
|
|
|
203
203
|
#
|
|
204
204
|
# === Yields
|
|
205
205
|
#
|
|
206
|
-
# [MiniMagick::Image] additional manipulations to perform
|
|
206
|
+
# [MiniMagick::Image] additional manipulations to perform (deprecated, use #manipulate!)
|
|
207
207
|
#
|
|
208
208
|
def resize_and_pad(width, height, background=:transparent, gravity='Center', combine_options: {}, &block)
|
|
209
209
|
width, height = resolve_dimensions(width, height)
|
|
@@ -228,7 +228,7 @@ module CarrierWave
|
|
|
228
228
|
#
|
|
229
229
|
# === Yields
|
|
230
230
|
#
|
|
231
|
-
# [MiniMagick::Image] additional manipulations to perform
|
|
231
|
+
# [MiniMagick::Image] additional manipulations to perform (deprecated, use #manipulate!)
|
|
232
232
|
#
|
|
233
233
|
def crop(left, top, width, height, combine_options: {}, &block)
|
|
234
234
|
width, height = resolve_dimensions(width, height)
|
|
@@ -290,6 +290,7 @@ module CarrierWave
|
|
|
290
290
|
|
|
291
291
|
image = yield(image)
|
|
292
292
|
FileUtils.mv image.path, current_path
|
|
293
|
+
follow_extension_of image.path
|
|
293
294
|
|
|
294
295
|
::MiniMagick::Image.new(current_path).identify
|
|
295
296
|
rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
|
|
@@ -325,20 +326,20 @@ module CarrierWave
|
|
|
325
326
|
result = builder.call
|
|
326
327
|
result.close
|
|
327
328
|
|
|
328
|
-
# backwards compatibility (we want to eventually move away from MiniMagick::Image)
|
|
329
329
|
if block
|
|
330
|
+
unless @minimagick_image_block_warned
|
|
331
|
+
@minimagick_image_block_warned = true
|
|
332
|
+
CarrierWave.deprecator.warn "Passing a block to receive a MiniMagick::Image is deprecated. " \
|
|
333
|
+
"Use #manipulate! to work with a MiniMagick::Image, or #minimagick! to build on the ImageProcessing builder."
|
|
334
|
+
end
|
|
335
|
+
|
|
330
336
|
image = ::MiniMagick::Image.new(result.path, result)
|
|
331
337
|
image = block.call(image)
|
|
332
338
|
result = image.instance_variable_get(:@tempfile)
|
|
333
339
|
end
|
|
334
340
|
|
|
335
341
|
FileUtils.mv result.path, current_path
|
|
336
|
-
|
|
337
|
-
if File.extname(result.path) != File.extname(current_path)
|
|
338
|
-
move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
|
|
339
|
-
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
|
|
340
|
-
file.move_to(move_to, permissions, directory_permissions)
|
|
341
|
-
end
|
|
342
|
+
follow_extension_of result.path
|
|
342
343
|
rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
|
|
343
344
|
raise e if e.message =~ /(You must have .+ installed|is not installed|executable not found)/
|
|
344
345
|
message = I18n.translate(:"errors.messages.processing_error")
|
|
@@ -347,6 +348,16 @@ module CarrierWave
|
|
|
347
348
|
|
|
348
349
|
private
|
|
349
350
|
|
|
351
|
+
# The processing may have changed the format, in which case the file is renamed so
|
|
352
|
+
# that its extension keeps telling what it actually holds
|
|
353
|
+
def follow_extension_of(processed_path)
|
|
354
|
+
return if File.extname(processed_path) == File.extname(current_path)
|
|
355
|
+
|
|
356
|
+
move_to = current_path.chomp(File.extname(current_path)) + File.extname(processed_path)
|
|
357
|
+
file.content_type = Marcel::Magic.by_path(move_to).try(:type)
|
|
358
|
+
file.move_to(move_to, permissions, directory_permissions)
|
|
359
|
+
end
|
|
360
|
+
|
|
350
361
|
def resolve_dimensions(*dimensions)
|
|
351
362
|
dimensions.map do |value|
|
|
352
363
|
next value unless value.instance_of?(Proc)
|
|
@@ -383,6 +383,11 @@ module CarrierWave
|
|
|
383
383
|
frame = yield(*[frame, index, options].take(block.arity)) if block_given?
|
|
384
384
|
frames << frame if frame
|
|
385
385
|
end
|
|
386
|
+
|
|
387
|
+
if frames.none?
|
|
388
|
+
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.unsupported_image_format_error")
|
|
389
|
+
end
|
|
390
|
+
|
|
386
391
|
frames.append(true) if block_given?
|
|
387
392
|
|
|
388
393
|
write_block = create_info_block(options[:write])
|
|
@@ -93,10 +93,6 @@ module CarrierWave
|
|
|
93
93
|
#
|
|
94
94
|
# [format (#to_s)] an abbreviation of the format
|
|
95
95
|
#
|
|
96
|
-
# === Yields
|
|
97
|
-
#
|
|
98
|
-
# [Vips::Image] additional manipulations to perform
|
|
99
|
-
#
|
|
100
96
|
# === Examples
|
|
101
97
|
#
|
|
102
98
|
# image.convert(:png)
|
|
@@ -121,10 +117,6 @@ module CarrierWave
|
|
|
121
117
|
# [height (Integer)] the height to scale the image to
|
|
122
118
|
# [combine_options (Hash)] additional Vips options to apply before resizing
|
|
123
119
|
#
|
|
124
|
-
# === Yields
|
|
125
|
-
#
|
|
126
|
-
# [Vips::Image] additional manipulations to perform
|
|
127
|
-
#
|
|
128
120
|
def resize_to_limit(width, height, combine_options: {})
|
|
129
121
|
width, height = resolve_dimensions(width, height)
|
|
130
122
|
|
|
@@ -145,10 +137,6 @@ module CarrierWave
|
|
|
145
137
|
# [height (Integer)] the height to scale the image to
|
|
146
138
|
# [combine_options (Hash)] additional Vips options to apply before resizing
|
|
147
139
|
#
|
|
148
|
-
# === Yields
|
|
149
|
-
#
|
|
150
|
-
# [Vips::Image] additional manipulations to perform
|
|
151
|
-
#
|
|
152
140
|
def resize_to_fit(width, height, combine_options: {})
|
|
153
141
|
width, height = resolve_dimensions(width, height)
|
|
154
142
|
|
|
@@ -169,10 +157,6 @@ module CarrierWave
|
|
|
169
157
|
# [height (Integer)] the height to scale the image to
|
|
170
158
|
# [combine_options (Hash)] additional vips options to apply before resizing
|
|
171
159
|
#
|
|
172
|
-
# === Yields
|
|
173
|
-
#
|
|
174
|
-
# [Vips::Image] additional manipulations to perform
|
|
175
|
-
#
|
|
176
160
|
def resize_to_fill(width, height, _gravity = nil, combine_options: {})
|
|
177
161
|
width, height = resolve_dimensions(width, height)
|
|
178
162
|
|
|
@@ -199,10 +183,6 @@ module CarrierWave
|
|
|
199
183
|
# [alpha (Boolean, nil)] pad the image with the alpha channel if supported
|
|
200
184
|
# [combine_options (Hash)] additional vips options to apply before resizing
|
|
201
185
|
#
|
|
202
|
-
# === Yields
|
|
203
|
-
#
|
|
204
|
-
# [Vips::Image] additional manipulations to perform
|
|
205
|
-
#
|
|
206
186
|
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil, combine_options: {})
|
|
207
187
|
width, height = resolve_dimensions(width, height)
|
|
208
188
|
|
|
@@ -224,10 +204,6 @@ module CarrierWave
|
|
|
224
204
|
# [width (Integer)] width of area to extract
|
|
225
205
|
# [height (Integer)] height of area to extract
|
|
226
206
|
#
|
|
227
|
-
# === Yields
|
|
228
|
-
#
|
|
229
|
-
# [Vips::Image] additional manipulations to perform
|
|
230
|
-
#
|
|
231
207
|
def crop(left, top, width, height, combine_options: {})
|
|
232
208
|
width, height = resolve_dimensions(width, height)
|
|
233
209
|
width = vips_image.width - left if width + left > vips_image.width
|
|
@@ -53,7 +53,7 @@ module CarrierWave
|
|
|
53
53
|
# [String] the sanitized filename
|
|
54
54
|
#
|
|
55
55
|
def filename
|
|
56
|
-
sanitize(original_filename)
|
|
56
|
+
sanitize(original_filename.to_s)
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
alias_method :identifier, :filename
|
|
@@ -305,7 +305,7 @@ module CarrierWave
|
|
|
305
305
|
|
|
306
306
|
# Sanitize the filename, to prevent hacking
|
|
307
307
|
def sanitize(name)
|
|
308
|
-
name = name.scrub
|
|
308
|
+
name = name.scrub.delete("\0") # a NUL byte cuts the path short, and File.basename refuses one
|
|
309
309
|
name = name.tr("\\", "/") # work-around for IE
|
|
310
310
|
name = File.basename(name)
|
|
311
311
|
name = name.gsub(sanitize_regexp, "_")
|
|
@@ -31,6 +31,15 @@ module CarrierWave
|
|
|
31
31
|
raise NotImplementedError, "Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage."
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
+
# Storage engines which defer writing to the cache have to flush it here
|
|
35
|
+
def materialize_cache!(file)
|
|
36
|
+
file
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def direct_upload_url(path, expires_in:, headers: {})
|
|
40
|
+
raise NotImplementedError, "Need to implement #direct_upload_url if you want to use #{self.class.name} for direct uploads."
|
|
41
|
+
end
|
|
42
|
+
|
|
34
43
|
def delete_dir!(path)
|
|
35
44
|
raise NotImplementedError, "Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage."
|
|
36
45
|
end
|
|
@@ -110,10 +110,8 @@ module CarrierWave
|
|
|
110
110
|
|
|
111
111
|
def clean_cache!(seconds)
|
|
112
112
|
Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), uploader.root)).each do |dir|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
next unless matched
|
|
116
|
-
time = Time.at(matched[0].to_i)
|
|
113
|
+
time = uploader.parse_cache_id(::File.basename(dir))
|
|
114
|
+
next unless time
|
|
117
115
|
if time < (Time.now.utc - seconds)
|
|
118
116
|
FileUtils.rm_rf(dir)
|
|
119
117
|
end
|
|
@@ -12,6 +12,13 @@ module CarrierWave
|
|
|
12
12
|
# [:fog_directory] specifies name of directory to store data in, assumed to already exist
|
|
13
13
|
#
|
|
14
14
|
# [:fog_attributes] (optional) additional attributes to set on files
|
|
15
|
+
# [:fog_acl] (optional) overrides the ACL set on stored/copied files.
|
|
16
|
+
# When nil (default), ACL is derived from fog_public (:public attribute passed to fog).
|
|
17
|
+
# When false, no ACL is set at all (omits :public; use for buckets/containers with ACLs disabled).
|
|
18
|
+
# When a String, used as the explicit ACL value: passed as x-amz-acl for AWS
|
|
19
|
+
# (e.g. 'bucket-owner-full-control') or destination_predefined_acl for Google
|
|
20
|
+
# (e.g. 'bucketOwnerFullControl'). For store operations, pair with fog_attributes to
|
|
21
|
+
# set the provider-specific header.
|
|
15
22
|
# [:fog_public] (optional) public readability, defaults to true
|
|
16
23
|
# [:fog_authenticated_url_expiration] (optional) time (in seconds) that authenticated urls
|
|
17
24
|
# will be valid, when fog_public is false and provider is AWS or Google, defaults to 600
|
|
@@ -105,7 +112,8 @@ module CarrierWave
|
|
|
105
112
|
end
|
|
106
113
|
|
|
107
114
|
##
|
|
108
|
-
#
|
|
115
|
+
# Stages given file to be cached, deferring the upload until #materialize_cache!,
|
|
116
|
+
# as a cached file usually goes straight to the store within the same request.
|
|
109
117
|
#
|
|
110
118
|
# === Parameters
|
|
111
119
|
#
|
|
@@ -113,12 +121,35 @@ module CarrierWave
|
|
|
113
121
|
#
|
|
114
122
|
# === Returns
|
|
115
123
|
#
|
|
116
|
-
# [CarrierWave::SanitizedFile]
|
|
124
|
+
# [CarrierWave::SanitizedFile] the staged file
|
|
125
|
+
# or
|
|
126
|
+
# [CarrierWave::Storage::Fog::File] the uploaded file, with cache_only
|
|
117
127
|
#
|
|
118
128
|
def cache!(new_file)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
129
|
+
# With cache_only there's no store to move to, so the cache is the final location
|
|
130
|
+
return upload_to_cache(new_file) if uploader.cache_only
|
|
131
|
+
|
|
132
|
+
local_storage.cache!(new_file)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
##
|
|
136
|
+
# Uploads the staged file to the cache directory.
|
|
137
|
+
#
|
|
138
|
+
# === Parameters
|
|
139
|
+
#
|
|
140
|
+
# [file (CarrierWave::SanitizedFile)] the staged file
|
|
141
|
+
#
|
|
142
|
+
# === Returns
|
|
143
|
+
#
|
|
144
|
+
# [CarrierWave::Storage::Fog::File] the uploaded file
|
|
145
|
+
#
|
|
146
|
+
def materialize_cache!(file)
|
|
147
|
+
return file if file.is_a?(CarrierWave::Storage::Fog::File)
|
|
148
|
+
|
|
149
|
+
upload_to_cache(file).tap do
|
|
150
|
+
file.delete
|
|
151
|
+
local_storage.delete_dir!(uploader.cache_path(nil))
|
|
152
|
+
end
|
|
122
153
|
end
|
|
123
154
|
|
|
124
155
|
##
|
|
@@ -140,22 +171,36 @@ module CarrierWave
|
|
|
140
171
|
# Deletes a cache dir
|
|
141
172
|
#
|
|
142
173
|
def delete_dir!(path)
|
|
143
|
-
#
|
|
174
|
+
# Only the local cache has dirs, as there's no such things as 'empty directory'
|
|
175
|
+
local_storage.delete_dir!(path)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
##
|
|
179
|
+
# Returns a URL a client can PUT the file to, to cache it without the bytes
|
|
180
|
+
# passing through the application.
|
|
181
|
+
#
|
|
182
|
+
def direct_upload_url(path, expires_in:, headers: {})
|
|
183
|
+
connection.put_object_url(uploader.fog_directory, path, ::Fog::Time.now.since(expires_in), headers)
|
|
144
184
|
end
|
|
145
185
|
|
|
146
186
|
def clean_cache!(seconds)
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
time =
|
|
187
|
+
local_storage.clean_cache!(seconds)
|
|
188
|
+
directory = connection.directories.new(fog_public_attrs.merge(:key => uploader.fog_directory))
|
|
189
|
+
prefix = "#{uploader.cache_dir.to_s.chomp('/')}/"
|
|
190
|
+
directory.files.all(:prefix => prefix).each do |file|
|
|
191
|
+
# the prefix is checked again, as some providers may not honor it when listing
|
|
192
|
+
next unless file.key.start_with?(prefix)
|
|
193
|
+
|
|
194
|
+
time = uploader.parse_cache_id(file.key.delete_prefix(prefix).split('/').first)
|
|
195
|
+
next unless time
|
|
155
196
|
file.destroy if time < (Time.now.utc - seconds)
|
|
156
197
|
end
|
|
157
198
|
end
|
|
158
199
|
|
|
200
|
+
def fog_public_attrs
|
|
201
|
+
uploader.fog_acl.nil? ? { :public => uploader.fog_public } : {}
|
|
202
|
+
end
|
|
203
|
+
|
|
159
204
|
def connection
|
|
160
205
|
@connection ||= begin
|
|
161
206
|
options = credentials = uploader.fog_credentials
|
|
@@ -296,7 +341,7 @@ module CarrierWave
|
|
|
296
341
|
return read_source_file if ::File.exist?(file_body.path)
|
|
297
342
|
|
|
298
343
|
# If the source file doesn't exist, the remote content is read
|
|
299
|
-
|
|
344
|
+
remove_instance_variable(:@file)
|
|
300
345
|
file.body
|
|
301
346
|
end
|
|
302
347
|
|
|
@@ -308,7 +353,7 @@ module CarrierWave
|
|
|
308
353
|
# [Integer] size of file body
|
|
309
354
|
#
|
|
310
355
|
def size
|
|
311
|
-
file
|
|
356
|
+
file&.content_length || 0
|
|
312
357
|
end
|
|
313
358
|
|
|
314
359
|
##
|
|
@@ -342,12 +387,11 @@ module CarrierWave
|
|
|
342
387
|
else
|
|
343
388
|
fog_file = new_file.to_file
|
|
344
389
|
@content_type ||= new_file.content_type
|
|
345
|
-
@file = directory.files.create(
|
|
390
|
+
@file = directory.files.create(fog_public_attrs.merge(
|
|
346
391
|
:body => fog_file || new_file.read,
|
|
347
392
|
:content_type => @content_type,
|
|
348
|
-
:key => path
|
|
349
|
-
|
|
350
|
-
}.merge(@uploader.fog_attributes))
|
|
393
|
+
:key => path
|
|
394
|
+
).merge(@uploader.fog_attributes))
|
|
351
395
|
fog_file.close if fog_file && !fog_file.closed?
|
|
352
396
|
end
|
|
353
397
|
true
|
|
@@ -439,11 +483,12 @@ module CarrierWave
|
|
|
439
483
|
#
|
|
440
484
|
def filename(options = {})
|
|
441
485
|
return unless (file_url = url(options))
|
|
442
|
-
|
|
486
|
+
decode_path(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
|
|
443
487
|
end
|
|
444
488
|
|
|
445
489
|
##
|
|
446
|
-
# Creates a copy of this file and returns it.
|
|
490
|
+
# Creates a copy of this file and returns it. Copying to where the file already is
|
|
491
|
+
# leaves it alone, as S3 refuses a copy onto the same key.
|
|
447
492
|
#
|
|
448
493
|
# === Parameters
|
|
449
494
|
#
|
|
@@ -454,7 +499,7 @@ module CarrierWave
|
|
|
454
499
|
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
|
455
500
|
#
|
|
456
501
|
def copy_to(new_path)
|
|
457
|
-
file.copy(@uploader.fog_directory, new_path, copy_options)
|
|
502
|
+
file.copy(@uploader.fog_directory, new_path, copy_options) unless new_path == path
|
|
458
503
|
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
|
459
504
|
end
|
|
460
505
|
|
|
@@ -498,10 +543,7 @@ module CarrierWave
|
|
|
498
543
|
# [Fog::#{provider}::Directory] containing directory
|
|
499
544
|
#
|
|
500
545
|
def directory
|
|
501
|
-
@directory ||= connection.directories.new(
|
|
502
|
-
:key => @uploader.fog_directory,
|
|
503
|
-
:public => @uploader.fog_public
|
|
504
|
-
)
|
|
546
|
+
@directory ||= connection.directories.new(fog_public_attrs.merge(:key => @uploader.fog_directory))
|
|
505
547
|
end
|
|
506
548
|
|
|
507
549
|
##
|
|
@@ -512,7 +554,9 @@ module CarrierWave
|
|
|
512
554
|
# [Fog::#{provider}::File] file data from remote service
|
|
513
555
|
#
|
|
514
556
|
def file
|
|
515
|
-
@file
|
|
557
|
+
return @file if defined?(@file)
|
|
558
|
+
|
|
559
|
+
@file = directory.files.head(path)
|
|
516
560
|
end
|
|
517
561
|
|
|
518
562
|
def copy_options
|
|
@@ -523,11 +567,19 @@ module CarrierWave
|
|
|
523
567
|
end
|
|
524
568
|
|
|
525
569
|
def acl_header
|
|
570
|
+
fog_acl = @uploader.fog_acl
|
|
571
|
+
return {} if fog_acl == false
|
|
572
|
+
|
|
526
573
|
case fog_provider
|
|
527
574
|
when 'AWS'
|
|
528
|
-
|
|
575
|
+
acl_value = fog_acl || (@uploader.fog_public ? 'public-read' : 'private')
|
|
576
|
+
{ 'x-amz-acl' => acl_value }
|
|
529
577
|
when "Google"
|
|
530
|
-
|
|
578
|
+
if fog_acl
|
|
579
|
+
{ destination_predefined_acl: fog_acl }
|
|
580
|
+
else
|
|
581
|
+
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
|
582
|
+
end
|
|
531
583
|
else
|
|
532
584
|
{}
|
|
533
585
|
end
|
|
@@ -537,6 +589,10 @@ module CarrierWave
|
|
|
537
589
|
@uploader.fog_credentials[:provider].to_s
|
|
538
590
|
end
|
|
539
591
|
|
|
592
|
+
def fog_public_attrs
|
|
593
|
+
@uploader.fog_acl.nil? ? { :public => @uploader.fog_public } : {}
|
|
594
|
+
end
|
|
595
|
+
|
|
540
596
|
def read_source_file
|
|
541
597
|
source_file = to_file
|
|
542
598
|
return unless source_file
|
|
@@ -554,6 +610,18 @@ module CarrierWave
|
|
|
554
610
|
end
|
|
555
611
|
end
|
|
556
612
|
|
|
613
|
+
private
|
|
614
|
+
|
|
615
|
+
def upload_to_cache(new_file)
|
|
616
|
+
f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.cache_path)
|
|
617
|
+
f.store(new_file)
|
|
618
|
+
f
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
def local_storage
|
|
622
|
+
@local_storage ||= CarrierWave::Storage::File.new(uploader)
|
|
623
|
+
end
|
|
624
|
+
|
|
557
625
|
end # Fog
|
|
558
626
|
|
|
559
627
|
end # Storage
|
|
@@ -336,6 +336,8 @@ module CarrierWave
|
|
|
336
336
|
def self.load_image(filename)
|
|
337
337
|
if defined? ::MiniMagick
|
|
338
338
|
MiniMagickWrapper.new(filename)
|
|
339
|
+
elsif defined? ::Vips
|
|
340
|
+
VipsWrapper.new(filename)
|
|
339
341
|
else
|
|
340
342
|
unless defined? ::Magick
|
|
341
343
|
begin
|
|
@@ -393,6 +395,28 @@ module CarrierWave
|
|
|
393
395
|
end
|
|
394
396
|
end
|
|
395
397
|
|
|
398
|
+
class VipsWrapper # :nodoc:
|
|
399
|
+
attr_reader :image
|
|
400
|
+
|
|
401
|
+
def width
|
|
402
|
+
image.width
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def height
|
|
406
|
+
image.height
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def format
|
|
410
|
+
# This returns the name of the vips loader (e.g. pngload) as Vips
|
|
411
|
+
# doesn't do content detection.
|
|
412
|
+
image.get("vips-loader").delete_suffix("load")
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
def initialize(filename)
|
|
416
|
+
@image = ::Vips::Image.new_from_file(filename)
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
396
420
|
end # Matchers
|
|
397
421
|
end # Test
|
|
398
422
|
end # CarrierWave
|
|
@@ -81,10 +81,9 @@ module CarrierWave
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
##
|
|
84
|
-
# Caches the
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
# require the file to be stored on the local filesystem.
|
|
84
|
+
# Caches the stored file, so that it can be processed. Most processing solutions
|
|
85
|
+
# require the file to be on the local filesystem. It is staged as any assigned
|
|
86
|
+
# file is, so the next save stores the result; use #read for a copy only to read.
|
|
88
87
|
#
|
|
89
88
|
def cache_stored_file!
|
|
90
89
|
cache!
|
|
@@ -103,7 +102,29 @@ module CarrierWave
|
|
|
103
102
|
# [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
|
|
104
103
|
#
|
|
105
104
|
def cache_name
|
|
106
|
-
File.join(cache_id, original_filename) if cache_id
|
|
105
|
+
File.join(*[cache_id, original_filename].compact) if cache_id
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
##
|
|
109
|
+
# Tells when the given cache id was created, and by returning nil, that it is
|
|
110
|
+
# not one. Override to take cache ids which were made elsewhere.
|
|
111
|
+
#
|
|
112
|
+
# === Parameters
|
|
113
|
+
#
|
|
114
|
+
# [cache_id (String)] the cache id to look at
|
|
115
|
+
#
|
|
116
|
+
# === Returns
|
|
117
|
+
#
|
|
118
|
+
# [Time] when it was created
|
|
119
|
+
# or
|
|
120
|
+
# [NilClass] if it isn't a cache id
|
|
121
|
+
#
|
|
122
|
+
def parse_cache_id(cache_id)
|
|
123
|
+
# Earlier version used 3 part cache_id. Thus we should allow for
|
|
124
|
+
# the cache_id to have both 3 part and 4 part formats.
|
|
125
|
+
return unless cache_id.to_s =~ /\A(-?\d+)-\d+(-\d{4})?-\d{4}\z/
|
|
126
|
+
|
|
127
|
+
Time.at($1.to_i)
|
|
107
128
|
end
|
|
108
129
|
|
|
109
130
|
##
|
|
@@ -146,11 +167,24 @@ module CarrierWave
|
|
|
146
167
|
with_callbacks(:cache, @file) do
|
|
147
168
|
@file = cache_storage.cache!(@file)
|
|
148
169
|
end
|
|
170
|
+
rescue Errno::ENAMETOOLONG
|
|
171
|
+
# How long a name may be is the filesystem's to say, so let it say it
|
|
172
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.filename_too_long_error")
|
|
149
173
|
ensure
|
|
150
174
|
FileUtils.rm_rf(workfile_path(''))
|
|
151
175
|
end
|
|
152
176
|
end
|
|
153
177
|
|
|
178
|
+
##
|
|
179
|
+
# Makes sure the cached file is written to the cache storage, which may have
|
|
180
|
+
# deferred it. Needed when the file has to outlive the current request.
|
|
181
|
+
#
|
|
182
|
+
def materialize_cache!
|
|
183
|
+
with_callbacks(:materialize_cache) do
|
|
184
|
+
@file = cache_storage.materialize_cache!(@file) if @file
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
154
188
|
##
|
|
155
189
|
# Retrieves the file with the given cache_name from the cache.
|
|
156
190
|
#
|
|
@@ -199,9 +233,7 @@ module CarrierWave
|
|
|
199
233
|
attr_reader :original_filename
|
|
200
234
|
|
|
201
235
|
def cache_id=(cache_id)
|
|
202
|
-
|
|
203
|
-
# the cache_id to have both 3 part and 4 part formats.
|
|
204
|
-
raise CarrierWave::InvalidParameter, "invalid cache id" unless cache_id =~ /\A(-)?[\d]+\-[\d]+(\-[\d]{4})?\-[\d]{4}\z/
|
|
236
|
+
raise CarrierWave::InvalidParameter, "invalid cache id" unless parse_cache_id(cache_id)
|
|
205
237
|
@cache_id = cache_id
|
|
206
238
|
end
|
|
207
239
|
|
|
@@ -211,7 +243,8 @@ module CarrierWave
|
|
|
211
243
|
end
|
|
212
244
|
|
|
213
245
|
def cache_storage
|
|
214
|
-
|
|
246
|
+
# Falls back to this uploader's storage, which may not be the configured one
|
|
247
|
+
@cache_storage ||= self.class.cache_storage&.new(self) || storage
|
|
215
248
|
end
|
|
216
249
|
|
|
217
250
|
# We can override the full_original_filename method in other modules
|