carrierwave 0.11.2 → 3.0.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of carrierwave might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/README.md +452 -178
- data/lib/carrierwave/compatibility/paperclip.rb +4 -4
- data/lib/carrierwave/downloader/base.rb +101 -0
- data/lib/carrierwave/downloader/remote_file.rb +68 -0
- data/lib/carrierwave/error.rb +1 -0
- data/lib/carrierwave/locale/en.yml +11 -5
- data/lib/carrierwave/mount.rb +212 -182
- data/lib/carrierwave/mounter.rb +255 -0
- data/lib/carrierwave/orm/activerecord.rb +22 -33
- data/lib/carrierwave/processing/mini_magick.rb +140 -84
- data/lib/carrierwave/processing/rmagick.rb +72 -21
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -1
- data/lib/carrierwave/sanitized_file.rb +83 -84
- data/lib/carrierwave/storage/abstract.rb +16 -3
- data/lib/carrierwave/storage/file.rb +71 -3
- data/lib/carrierwave/storage/fog.rb +215 -57
- data/lib/carrierwave/storage.rb +1 -9
- data/lib/carrierwave/test/matchers.rb +88 -19
- data/lib/carrierwave/uploader/cache.rb +75 -45
- data/lib/carrierwave/uploader/callbacks.rb +1 -3
- data/lib/carrierwave/uploader/configuration.rb +80 -16
- data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
- data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
- data/lib/carrierwave/uploader/default_url.rb +3 -5
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/download.rb +4 -74
- data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
- data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
- data/lib/carrierwave/uploader/file_size.rb +43 -0
- data/lib/carrierwave/uploader/mountable.rb +13 -8
- data/lib/carrierwave/uploader/processing.rb +48 -13
- data/lib/carrierwave/uploader/proxy.rb +20 -9
- data/lib/carrierwave/uploader/remove.rb +0 -2
- data/lib/carrierwave/uploader/serialization.rb +2 -4
- data/lib/carrierwave/uploader/store.rb +59 -28
- data/lib/carrierwave/uploader/url.rb +8 -7
- data/lib/carrierwave/uploader/versions.rb +170 -122
- data/lib/carrierwave/uploader.rb +12 -10
- data/lib/carrierwave/utilities/file_name.rb +47 -0
- data/lib/carrierwave/utilities/uri.rb +14 -12
- data/lib/carrierwave/utilities.rb +1 -3
- data/lib/carrierwave/validations/active_model.rb +7 -11
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +39 -21
- data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +5 -9
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +132 -80
- data/lib/carrierwave/locale/cs.yml +0 -11
- data/lib/carrierwave/locale/de.yml +0 -11
- data/lib/carrierwave/locale/el.yml +0 -11
- data/lib/carrierwave/locale/es.yml +0 -11
- data/lib/carrierwave/locale/fr.yml +0 -11
- data/lib/carrierwave/locale/ja.yml +0 -11
- data/lib/carrierwave/locale/nb.yml +0 -11
- data/lib/carrierwave/locale/nl.yml +0 -11
- data/lib/carrierwave/locale/pl.yml +0 -11
- data/lib/carrierwave/locale/pt-BR.yml +0 -11
- data/lib/carrierwave/locale/pt-PT.yml +0 -11
- data/lib/carrierwave/locale/ru.yml +0 -11
- data/lib/carrierwave/locale/sk.yml +0 -11
- data/lib/carrierwave/locale/tr.yml +0 -11
- data/lib/carrierwave/processing/mime_types.rb +0 -74
- data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
- data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
- data/lib/carrierwave/uploader/extension_blacklist.rb +0 -47
- data/lib/carrierwave/uploader/extension_whitelist.rb +0 -49
- data/lib/carrierwave/utilities/deprecation.rb +0 -18
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
module Storage
|
5
3
|
|
@@ -18,6 +16,8 @@ module CarrierWave
|
|
18
16
|
# [:fog_authenticated_url_expiration] (optional) time (in seconds) that authenticated urls
|
19
17
|
# will be valid, when fog_public is false and provider is AWS or Google, defaults to 600
|
20
18
|
# [:fog_use_ssl_for_aws] (optional) #public_url will use https for the AWS generated URL]
|
19
|
+
# [:fog_aws_accelerate] (optional) #public_url will use s3-accelerate subdomain
|
20
|
+
# instead of s3, defaults to false
|
21
21
|
#
|
22
22
|
#
|
23
23
|
# AWS credentials contain the following keys:
|
@@ -25,12 +25,12 @@ module CarrierWave
|
|
25
25
|
# [:aws_access_key_id]
|
26
26
|
# [:aws_secret_access_key]
|
27
27
|
# [:region] (optional) defaults to 'us-east-1'
|
28
|
-
# :region should be one of ['eu-west-1', 'us-east-1', 'ap-southeast-1', 'us-west-1', 'ap-northeast-1']
|
28
|
+
# :region should be one of ['eu-west-1', 'us-east-1', 'ap-southeast-1', 'us-west-1', 'ap-northeast-1', 'eu-central-1']
|
29
29
|
#
|
30
30
|
#
|
31
31
|
# Google credentials contain the following keys:
|
32
32
|
# [:google_storage_access_key_id]
|
33
|
-
# [:
|
33
|
+
# [:google_storage_secret_access_key]
|
34
34
|
#
|
35
35
|
#
|
36
36
|
# Local credentials contain the following keys:
|
@@ -60,6 +60,14 @@ module CarrierWave
|
|
60
60
|
def connection_cache
|
61
61
|
@connection_cache ||= {}
|
62
62
|
end
|
63
|
+
|
64
|
+
def eager_load
|
65
|
+
# see #1198. This will hopefully no longer be necessary in future release of fog
|
66
|
+
fog_credentials = CarrierWave::Uploader::Base.fog_credentials
|
67
|
+
if fog_credentials.present?
|
68
|
+
CarrierWave::Storage::Fog.connection_cache[fog_credentials] ||= ::Fog::Storage.new(fog_credentials)
|
69
|
+
end
|
70
|
+
end
|
63
71
|
end
|
64
72
|
|
65
73
|
##
|
@@ -94,6 +102,58 @@ module CarrierWave
|
|
94
102
|
CarrierWave::Storage::Fog::File.new(uploader, self, uploader.store_path(identifier))
|
95
103
|
end
|
96
104
|
|
105
|
+
##
|
106
|
+
# Stores given file to cache directory.
|
107
|
+
#
|
108
|
+
# === Parameters
|
109
|
+
#
|
110
|
+
# [new_file (File, IOString, Tempfile)] any kind of file object
|
111
|
+
#
|
112
|
+
# === Returns
|
113
|
+
#
|
114
|
+
# [CarrierWave::SanitizedFile] a sanitized file
|
115
|
+
#
|
116
|
+
def cache!(new_file)
|
117
|
+
f = CarrierWave::Storage::Fog::File.new(uploader, self, uploader.cache_path)
|
118
|
+
f.store(new_file)
|
119
|
+
f
|
120
|
+
end
|
121
|
+
|
122
|
+
##
|
123
|
+
# Retrieves the file with the given cache_name from the cache.
|
124
|
+
#
|
125
|
+
# === Parameters
|
126
|
+
#
|
127
|
+
# [cache_name (String)] uniquely identifies a cache file
|
128
|
+
#
|
129
|
+
# === Raises
|
130
|
+
#
|
131
|
+
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
|
132
|
+
#
|
133
|
+
def retrieve_from_cache!(identifier)
|
134
|
+
CarrierWave::Storage::Fog::File.new(uploader, self, uploader.cache_path(identifier))
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Deletes a cache dir
|
139
|
+
#
|
140
|
+
def delete_dir!(path)
|
141
|
+
# do nothing, because there's no such things as 'empty directory'
|
142
|
+
end
|
143
|
+
|
144
|
+
def clean_cache!(seconds)
|
145
|
+
connection.directories.new(
|
146
|
+
:key => uploader.fog_directory,
|
147
|
+
:public => uploader.fog_public
|
148
|
+
).files.all(:prefix => uploader.cache_dir).each do |file|
|
149
|
+
# generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
|
150
|
+
matched = file.key.match(/(\d+)-\d+-\d+(?:-\d+)?/)
|
151
|
+
next unless matched
|
152
|
+
time = Time.at(matched[1].to_i)
|
153
|
+
file.destroy if time < (Time.now.utc - seconds)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
97
157
|
def connection
|
98
158
|
@connection ||= begin
|
99
159
|
options = credentials = uploader.fog_credentials
|
@@ -102,7 +162,10 @@ module CarrierWave
|
|
102
162
|
end
|
103
163
|
|
104
164
|
class File
|
165
|
+
DEFAULT_S3_REGION = 'us-east-1'.freeze
|
166
|
+
|
105
167
|
include CarrierWave::Utilities::Uri
|
168
|
+
include CarrierWave::Utilities::FileName
|
106
169
|
|
107
170
|
##
|
108
171
|
# Current local path to file
|
@@ -126,7 +189,7 @@ module CarrierWave
|
|
126
189
|
|
127
190
|
##
|
128
191
|
# Return a temporary authenticated url to a private file, if available
|
129
|
-
# Only supported for AWS, Rackspace and
|
192
|
+
# Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
|
130
193
|
#
|
131
194
|
# === Returns
|
132
195
|
#
|
@@ -135,19 +198,28 @@ module CarrierWave
|
|
135
198
|
# [NilClass] no authenticated url available
|
136
199
|
#
|
137
200
|
def authenticated_url(options = {})
|
138
|
-
if ['AWS', 'Google', 'Rackspace', 'OpenStack'].include?(
|
201
|
+
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(fog_provider)
|
139
202
|
# avoid a get by using local references
|
140
203
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
141
204
|
local_file = local_directory.files.new(:key => path)
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
205
|
+
expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
|
206
|
+
case fog_provider
|
207
|
+
when 'AWS', 'Google'
|
208
|
+
# Older versions of fog-google do not support options as a parameter
|
209
|
+
if url_options_supported?(local_file)
|
210
|
+
local_file.url(expire_at, options)
|
211
|
+
else
|
212
|
+
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
213
|
+
local_file.url(expire_at)
|
214
|
+
end
|
215
|
+
when 'Rackspace', 'OpenStack'
|
216
|
+
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
|
217
|
+
when 'Aliyun'
|
218
|
+
expire_at -= Time.now
|
219
|
+
local_file.url(expire_at)
|
146
220
|
else
|
147
|
-
local_file.url(
|
221
|
+
local_file.url(expire_at)
|
148
222
|
end
|
149
|
-
else
|
150
|
-
nil
|
151
223
|
end
|
152
224
|
end
|
153
225
|
|
@@ -159,7 +231,7 @@ module CarrierWave
|
|
159
231
|
# [String] value of content-type
|
160
232
|
#
|
161
233
|
def content_type
|
162
|
-
@content_type || file.content_type
|
234
|
+
@content_type || file.try(:content_type)
|
163
235
|
end
|
164
236
|
|
165
237
|
##
|
@@ -182,19 +254,9 @@ module CarrierWave
|
|
182
254
|
#
|
183
255
|
def delete
|
184
256
|
# avoid a get by just using local reference
|
185
|
-
directory.files.new(:key => path).destroy
|
186
|
-
|
187
|
-
|
188
|
-
##
|
189
|
-
# Return extension of file
|
190
|
-
#
|
191
|
-
# === Returns
|
192
|
-
#
|
193
|
-
# [String] extension of file or nil if the file has no extension
|
194
|
-
#
|
195
|
-
def extension
|
196
|
-
path_elements = path.split('.')
|
197
|
-
path_elements.last if path_elements.size > 1
|
257
|
+
directory.files.new(:key => path).destroy.tap do |result|
|
258
|
+
@file = nil if result
|
259
|
+
end
|
198
260
|
end
|
199
261
|
|
200
262
|
##
|
@@ -213,7 +275,7 @@ module CarrierWave
|
|
213
275
|
end
|
214
276
|
|
215
277
|
def initialize(uploader, base, path)
|
216
|
-
@uploader, @base, @path = uploader, base, path
|
278
|
+
@uploader, @base, @path, @content_type = uploader, base, path, nil
|
217
279
|
end
|
218
280
|
|
219
281
|
##
|
@@ -223,6 +285,16 @@ module CarrierWave
|
|
223
285
|
#
|
224
286
|
# [String] contents of file
|
225
287
|
def read
|
288
|
+
file_body = file&.body
|
289
|
+
|
290
|
+
return if file_body.nil?
|
291
|
+
return file_body unless file_body.is_a?(::File)
|
292
|
+
|
293
|
+
# Fog::Storage::XXX::File#body could return the source file which was uploaded to the remote server.
|
294
|
+
return read_source_file if ::File.exist?(file_body.path)
|
295
|
+
|
296
|
+
# If the source file doesn't exist, the remote content is read
|
297
|
+
@file = nil
|
226
298
|
file.body
|
227
299
|
end
|
228
300
|
|
@@ -234,7 +306,7 @@ module CarrierWave
|
|
234
306
|
# [Integer] size of file body
|
235
307
|
#
|
236
308
|
def size
|
237
|
-
file.content_length
|
309
|
+
file.nil? ? 0 : file.content_length
|
238
310
|
end
|
239
311
|
|
240
312
|
##
|
@@ -244,7 +316,7 @@ module CarrierWave
|
|
244
316
|
#
|
245
317
|
# [Boolean] true if file exists or false
|
246
318
|
def exists?
|
247
|
-
!!
|
319
|
+
!!file
|
248
320
|
end
|
249
321
|
|
250
322
|
##
|
@@ -254,15 +326,19 @@ module CarrierWave
|
|
254
326
|
#
|
255
327
|
# [Boolean] true on success or raises error
|
256
328
|
def store(new_file)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
329
|
+
if new_file.is_a?(self.class)
|
330
|
+
new_file.copy_to(path)
|
331
|
+
else
|
332
|
+
fog_file = new_file.to_file
|
333
|
+
@content_type ||= new_file.content_type
|
334
|
+
@file = directory.files.create({
|
335
|
+
:body => fog_file || new_file.read,
|
336
|
+
:content_type => @content_type,
|
337
|
+
:key => path,
|
338
|
+
:public => @uploader.fog_public
|
339
|
+
}.merge(@uploader.fog_attributes))
|
340
|
+
fog_file.close if fog_file && !fog_file.closed?
|
341
|
+
end
|
266
342
|
true
|
267
343
|
end
|
268
344
|
|
@@ -277,7 +353,7 @@ module CarrierWave
|
|
277
353
|
#
|
278
354
|
def public_url
|
279
355
|
encoded_path = encode_path(path)
|
280
|
-
if host = @uploader.asset_host
|
356
|
+
if (host = @uploader.asset_host)
|
281
357
|
if host.respond_to? :call
|
282
358
|
"#{host.call(self)}/#{encoded_path}"
|
283
359
|
else
|
@@ -285,23 +361,36 @@ module CarrierWave
|
|
285
361
|
end
|
286
362
|
else
|
287
363
|
# AWS/Google optimized for speed over correctness
|
288
|
-
case
|
364
|
+
case fog_provider
|
289
365
|
when 'AWS'
|
290
366
|
# check if some endpoint is set in fog_credentials
|
291
367
|
if @uploader.fog_credentials.has_key?(:endpoint)
|
292
368
|
"#{@uploader.fog_credentials[:endpoint]}/#{@uploader.fog_directory}/#{encoded_path}"
|
293
369
|
else
|
294
370
|
protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
371
|
+
|
372
|
+
subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
|
373
|
+
# To use the virtual-hosted style, the bucket name needs to be representable as a subdomain
|
374
|
+
use_virtual_hosted_style = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
|
375
|
+
|
376
|
+
region = @uploader.fog_credentials[:region].to_s
|
377
|
+
regional_host = case region
|
378
|
+
when DEFAULT_S3_REGION, ''
|
379
|
+
's3.amazonaws.com'
|
380
|
+
else
|
381
|
+
"s3.#{region}.amazonaws.com"
|
382
|
+
end
|
383
|
+
|
384
|
+
if use_virtual_hosted_style
|
385
|
+
regional_host = 's3-accelerate.amazonaws.com' if @uploader.fog_aws_accelerate
|
386
|
+
"#{protocol}://#{@uploader.fog_directory}.#{regional_host}/#{encoded_path}"
|
387
|
+
else # directory is not a valid subdomain, so use path style for access
|
388
|
+
"#{protocol}://#{regional_host}/#{@uploader.fog_directory}/#{encoded_path}"
|
301
389
|
end
|
302
390
|
end
|
303
391
|
when 'Google'
|
304
|
-
|
392
|
+
# https://cloud.google.com/storage/docs/access-public-data
|
393
|
+
"https://storage.googleapis.com/#{@uploader.fog_directory}/#{encoded_path}"
|
305
394
|
else
|
306
395
|
# avoid a get by just using local reference
|
307
396
|
directory.files.new(:key => path).public_url
|
@@ -310,7 +399,7 @@ module CarrierWave
|
|
310
399
|
end
|
311
400
|
|
312
401
|
##
|
313
|
-
# Return url to file, if
|
402
|
+
# Return url to file, if available
|
314
403
|
#
|
315
404
|
# === Returns
|
316
405
|
#
|
@@ -336,8 +425,42 @@ module CarrierWave
|
|
336
425
|
# [NilClass] no file name available
|
337
426
|
#
|
338
427
|
def filename(options = {})
|
339
|
-
|
340
|
-
|
428
|
+
return unless (file_url = url(options))
|
429
|
+
CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
|
430
|
+
end
|
431
|
+
|
432
|
+
##
|
433
|
+
# Creates a copy of this file and returns it.
|
434
|
+
#
|
435
|
+
# === Parameters
|
436
|
+
#
|
437
|
+
# [new_path (String)] The path where the file should be copied to.
|
438
|
+
#
|
439
|
+
# === Returns
|
440
|
+
#
|
441
|
+
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
442
|
+
#
|
443
|
+
def copy_to(new_path)
|
444
|
+
file.copy(@uploader.fog_directory, new_path, copy_options)
|
445
|
+
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
446
|
+
end
|
447
|
+
|
448
|
+
##
|
449
|
+
# Return the local file
|
450
|
+
#
|
451
|
+
# === Returns
|
452
|
+
#
|
453
|
+
# [File] The local file as Ruby's File class
|
454
|
+
# or
|
455
|
+
# [NilClass] When there's no file, or the file is remotely stored
|
456
|
+
#
|
457
|
+
def to_file
|
458
|
+
return nil unless file.body.is_a? ::File
|
459
|
+
|
460
|
+
if file.body.closed?
|
461
|
+
::File.open(file.body.path) # Reopen if it's already closed
|
462
|
+
else
|
463
|
+
file.body
|
341
464
|
end
|
342
465
|
end
|
343
466
|
|
@@ -362,12 +485,10 @@ module CarrierWave
|
|
362
485
|
# [Fog::#{provider}::Directory] containing directory
|
363
486
|
#
|
364
487
|
def directory
|
365
|
-
@directory ||=
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
)
|
370
|
-
end
|
488
|
+
@directory ||= connection.directories.new(
|
489
|
+
:key => @uploader.fog_directory,
|
490
|
+
:public => @uploader.fog_public
|
491
|
+
)
|
371
492
|
end
|
372
493
|
|
373
494
|
##
|
@@ -381,6 +502,43 @@ module CarrierWave
|
|
381
502
|
@file ||= directory.files.head(path)
|
382
503
|
end
|
383
504
|
|
505
|
+
def copy_options
|
506
|
+
options = {}
|
507
|
+
options.merge!(acl_header) if acl_header.present?
|
508
|
+
options[fog_provider == "Google" ? :content_type : 'Content-Type'] ||= content_type if content_type
|
509
|
+
options.merge(@uploader.fog_attributes)
|
510
|
+
end
|
511
|
+
|
512
|
+
def acl_header
|
513
|
+
case fog_provider
|
514
|
+
when 'AWS'
|
515
|
+
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
516
|
+
when "Google"
|
517
|
+
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
518
|
+
else
|
519
|
+
{}
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
def fog_provider
|
524
|
+
@uploader.fog_credentials[:provider].to_s
|
525
|
+
end
|
526
|
+
|
527
|
+
def read_source_file
|
528
|
+
source_file = to_file
|
529
|
+
return unless source_file
|
530
|
+
|
531
|
+
begin
|
532
|
+
source_file.read
|
533
|
+
ensure
|
534
|
+
source_file.close
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
def url_options_supported?(local_file)
|
539
|
+
parameters = local_file.method(:url).parameters
|
540
|
+
parameters.count == 2 && parameters[1].include?(:options)
|
541
|
+
end
|
384
542
|
end
|
385
543
|
|
386
544
|
end # Fog
|
data/lib/carrierwave/storage.rb
CHANGED
@@ -1,11 +1,3 @@
|
|
1
1
|
require "carrierwave/storage/abstract"
|
2
2
|
require "carrierwave/storage/file"
|
3
|
-
|
4
|
-
%w(aws google openstack rackspace).each do |fog_dependency|
|
5
|
-
begin
|
6
|
-
require "fog/#{fog_dependency}"
|
7
|
-
rescue LoadError
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
require "carrierwave/storage/fog" if defined?(Fog)
|
3
|
+
require "carrierwave/storage/fog"
|