carrierwave 2.2.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.
Potentially problematic release.
This version of carrierwave might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +151 -63
- data/lib/carrierwave/compatibility/paperclip.rb +4 -2
- data/lib/carrierwave/downloader/base.rb +27 -13
- data/lib/carrierwave/downloader/remote_file.rb +12 -9
- data/lib/carrierwave/locale/en.yml +5 -3
- data/lib/carrierwave/mount.rb +31 -50
- data/lib/carrierwave/mounter.rb +115 -50
- data/lib/carrierwave/orm/activerecord.rb +12 -60
- data/lib/carrierwave/processing/mini_magick.rb +15 -13
- data/lib/carrierwave/processing/rmagick.rb +11 -15
- data/lib/carrierwave/processing/vips.rb +12 -12
- data/lib/carrierwave/sanitized_file.rb +50 -79
- data/lib/carrierwave/storage/abstract.rb +5 -5
- data/lib/carrierwave/storage/file.rb +6 -5
- data/lib/carrierwave/storage/fog.rb +78 -69
- data/lib/carrierwave/test/matchers.rb +11 -7
- data/lib/carrierwave/uploader/cache.rb +18 -10
- data/lib/carrierwave/uploader/callbacks.rb +1 -1
- data/lib/carrierwave/uploader/configuration.rb +10 -4
- data/lib/carrierwave/uploader/{content_type_whitelist.rb → content_type_allowlist.rb} +17 -15
- data/lib/carrierwave/uploader/{content_type_blacklist.rb → content_type_denylist.rb} +19 -14
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/{extension_whitelist.rb → extension_allowlist.rb} +17 -15
- data/lib/carrierwave/uploader/{extension_blacklist.rb → extension_denylist.rb} +18 -13
- data/lib/carrierwave/uploader/file_size.rb +2 -2
- data/lib/carrierwave/uploader/processing.rb +42 -7
- data/lib/carrierwave/uploader/proxy.rb +16 -3
- data/lib/carrierwave/uploader/store.rb +43 -6
- data/lib/carrierwave/uploader/url.rb +1 -1
- data/lib/carrierwave/uploader/versions.rb +137 -132
- data/lib/carrierwave/uploader.rb +10 -8
- data/lib/carrierwave/utilities/file_name.rb +47 -0
- data/lib/carrierwave/utilities/uri.rb +14 -11
- data/lib/carrierwave/utilities.rb +1 -0
- data/lib/carrierwave/validations/active_model.rb +4 -6
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +9 -17
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +54 -46
- /data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +0 -0
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'active_support/core_ext/string/multibyte'
|
3
|
-
require '
|
4
|
-
require 'mimemagic'
|
5
|
-
require 'mimemagic/overlay'
|
3
|
+
require 'marcel'
|
6
4
|
|
7
5
|
module CarrierWave
|
8
6
|
|
@@ -15,6 +13,7 @@ module CarrierWave
|
|
15
13
|
# It's probably needlessly comprehensive and complex. Help is appreciated.
|
16
14
|
#
|
17
15
|
class SanitizedFile
|
16
|
+
include CarrierWave::Utilities::FileName
|
18
17
|
|
19
18
|
attr_reader :file
|
20
19
|
|
@@ -28,7 +27,7 @@ module CarrierWave
|
|
28
27
|
|
29
28
|
def initialize(file)
|
30
29
|
self.file = file
|
31
|
-
@content = nil
|
30
|
+
@content = @content_type = nil
|
32
31
|
end
|
33
32
|
|
34
33
|
##
|
@@ -40,7 +39,7 @@ module CarrierWave
|
|
40
39
|
#
|
41
40
|
def original_filename
|
42
41
|
return @original_filename if @original_filename
|
43
|
-
if @file
|
42
|
+
if @file && @file.respond_to?(:original_filename)
|
44
43
|
@file.original_filename
|
45
44
|
elsif path
|
46
45
|
File.basename(path)
|
@@ -60,29 +59,6 @@ module CarrierWave
|
|
60
59
|
|
61
60
|
alias_method :identifier, :filename
|
62
61
|
|
63
|
-
##
|
64
|
-
# Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
|
65
|
-
# this would return 'test'
|
66
|
-
#
|
67
|
-
# === Returns
|
68
|
-
#
|
69
|
-
# [String] the first part of the filename
|
70
|
-
#
|
71
|
-
def basename
|
72
|
-
split_extension(filename)[0] if filename
|
73
|
-
end
|
74
|
-
|
75
|
-
##
|
76
|
-
# Returns the file extension
|
77
|
-
#
|
78
|
-
# === Returns
|
79
|
-
#
|
80
|
-
# [String] the extension
|
81
|
-
#
|
82
|
-
def extension
|
83
|
-
split_extension(filename)[1] if filename
|
84
|
-
end
|
85
|
-
|
86
62
|
##
|
87
63
|
# Returns the file's size.
|
88
64
|
#
|
@@ -133,7 +109,7 @@ module CarrierWave
|
|
133
109
|
# [Boolean] whether the file is valid and has a non-zero size
|
134
110
|
#
|
135
111
|
def empty?
|
136
|
-
@file.nil? || self.size.nil? || (self.size.zero? && !
|
112
|
+
@file.nil? || self.size.nil? || (self.size.zero? && !self.exists?)
|
137
113
|
end
|
138
114
|
|
139
115
|
##
|
@@ -152,15 +128,21 @@ module CarrierWave
|
|
152
128
|
#
|
153
129
|
# [String] contents of the file
|
154
130
|
#
|
155
|
-
def read
|
131
|
+
def read(*args)
|
156
132
|
if @content
|
157
|
-
|
133
|
+
if args.empty?
|
134
|
+
@content
|
135
|
+
else
|
136
|
+
length, outbuf = args
|
137
|
+
raise ArgumentError, "outbuf argument not supported since the content is already loaded" if outbuf
|
138
|
+
@content[0, length]
|
139
|
+
end
|
158
140
|
elsif is_path?
|
159
|
-
File.open(@file, "rb") {|file| file.read}
|
141
|
+
File.open(@file, "rb") {|file| file.read(*args)}
|
160
142
|
else
|
161
143
|
@file.try(:rewind)
|
162
|
-
@content = @file.read
|
163
|
-
@file.try(:close) unless @file.try(:closed?)
|
144
|
+
@content = @file.read(*args)
|
145
|
+
@file.try(:close) unless @file.class.ancestors.include?(::StringIO) || @file.try(:closed?)
|
164
146
|
@content
|
165
147
|
end
|
166
148
|
end
|
@@ -181,13 +163,10 @@ module CarrierWave
|
|
181
163
|
mkdir!(new_path, directory_permissions)
|
182
164
|
move!(new_path)
|
183
165
|
chmod!(new_path, permissions)
|
184
|
-
|
185
|
-
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
|
186
|
-
else
|
187
|
-
self.file = {:tempfile => new_path, :content_type => @content_type}
|
188
|
-
end
|
166
|
+
self.file = {tempfile: new_path, filename: keep_filename ? original_filename : nil, content_type: declared_content_type}
|
189
167
|
self
|
190
168
|
end
|
169
|
+
|
191
170
|
##
|
192
171
|
# Helper to move file to new path.
|
193
172
|
#
|
@@ -219,7 +198,7 @@ module CarrierWave
|
|
219
198
|
mkdir!(new_path, directory_permissions)
|
220
199
|
copy!(new_path)
|
221
200
|
chmod!(new_path, permissions)
|
222
|
-
self.class.new({:
|
201
|
+
self.class.new({tempfile: new_path, content_type: declared_content_type})
|
223
202
|
end
|
224
203
|
|
225
204
|
##
|
@@ -261,9 +240,10 @@ module CarrierWave
|
|
261
240
|
#
|
262
241
|
def content_type
|
263
242
|
@content_type ||=
|
264
|
-
|
265
|
-
|
266
|
-
|
243
|
+
identified_content_type ||
|
244
|
+
declared_content_type ||
|
245
|
+
guessed_safe_content_type ||
|
246
|
+
Marcel::MimeType::BINARY
|
267
247
|
end
|
268
248
|
|
269
249
|
##
|
@@ -294,11 +274,11 @@ module CarrierWave
|
|
294
274
|
if file.is_a?(Hash)
|
295
275
|
@file = file["tempfile"] || file[:tempfile]
|
296
276
|
@original_filename = file["filename"] || file[:filename]
|
297
|
-
@
|
277
|
+
@declared_content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
|
298
278
|
else
|
299
279
|
@file = file
|
300
280
|
@original_filename = nil
|
301
|
-
@
|
281
|
+
@declared_content_type = nil
|
302
282
|
end
|
303
283
|
end
|
304
284
|
|
@@ -315,57 +295,48 @@ module CarrierWave
|
|
315
295
|
|
316
296
|
# Sanitize the filename, to prevent hacking
|
317
297
|
def sanitize(name)
|
298
|
+
name = name.scrub
|
318
299
|
name = name.tr("\\", "/") # work-around for IE
|
319
300
|
name = File.basename(name)
|
320
|
-
name = name.gsub(sanitize_regexp,"_")
|
301
|
+
name = name.gsub(sanitize_regexp, "_")
|
321
302
|
name = "_#{name}" if name =~ /\A\.+\z/
|
322
303
|
name = "unnamed" if name.size.zero?
|
323
|
-
|
304
|
+
name.mb_chars.to_s
|
324
305
|
end
|
325
306
|
|
326
|
-
def
|
327
|
-
|
328
|
-
@file.content_type.
|
329
|
-
|
307
|
+
def declared_content_type
|
308
|
+
@declared_content_type ||
|
309
|
+
if @file.respond_to?(:content_type) && @file.content_type
|
310
|
+
@file.content_type.to_s.chomp
|
311
|
+
end
|
330
312
|
end
|
331
313
|
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
MimeMagic.by_magic(file).try(:type)
|
336
|
-
end
|
314
|
+
# Guess content type from its file extension. Limit what to be returned to prevent spoofing.
|
315
|
+
def guessed_safe_content_type
|
316
|
+
return unless path
|
337
317
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
end
|
318
|
+
type = Marcel::Magic.by_path(original_filename).to_s
|
319
|
+
type if type.start_with?('text/') || type.start_with?('application/json')
|
320
|
+
end
|
342
321
|
|
343
|
-
|
322
|
+
def identified_content_type
|
323
|
+
with_io do |io|
|
324
|
+
Marcel::Magic.by_magic(io).try(:type)
|
344
325
|
end
|
345
326
|
rescue Errno::ENOENT
|
346
327
|
nil
|
347
328
|
end
|
348
329
|
|
349
|
-
def
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
def split_extension(filename)
|
356
|
-
# regular expressions to try for identifying extensions
|
357
|
-
extension_matchers = [
|
358
|
-
/\A(.+)\.(tar\.([glx]?z|bz2))\z/, # matches "something.tar.gz"
|
359
|
-
/\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
|
360
|
-
]
|
361
|
-
|
362
|
-
extension_matchers.each do |regexp|
|
363
|
-
if filename =~ regexp
|
364
|
-
return $1, $2
|
330
|
+
def with_io(&block)
|
331
|
+
if file.is_a?(IO)
|
332
|
+
begin
|
333
|
+
yield file
|
334
|
+
ensure
|
335
|
+
file.try(:rewind)
|
365
336
|
end
|
337
|
+
elsif path
|
338
|
+
File.open(path, &block)
|
366
339
|
end
|
367
|
-
return filename, "" # In case we weren't able to split the extension
|
368
340
|
end
|
369
|
-
|
370
341
|
end # SanitizedFile
|
371
342
|
end # CarrierWave
|
@@ -14,7 +14,7 @@ module CarrierWave
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def identifier
|
17
|
-
uploader.
|
17
|
+
uploader.deduplicated_filename
|
18
18
|
end
|
19
19
|
|
20
20
|
def store!(file)
|
@@ -24,19 +24,19 @@ module CarrierWave
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def cache!(new_file)
|
27
|
-
raise NotImplementedError
|
27
|
+
raise NotImplementedError, "Need to implement #cache! if you want to use #{self.class.name} as a cache storage."
|
28
28
|
end
|
29
29
|
|
30
30
|
def retrieve_from_cache!(identifier)
|
31
|
-
raise NotImplementedError
|
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
34
|
def delete_dir!(path)
|
35
|
-
raise NotImplementedError
|
35
|
+
raise NotImplementedError, "Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage."
|
36
36
|
end
|
37
37
|
|
38
38
|
def clean_cache!(seconds)
|
39
|
-
raise NotImplementedError
|
39
|
+
raise NotImplementedError, "Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage."
|
40
40
|
end
|
41
41
|
end # Abstract
|
42
42
|
end # Storage
|
@@ -17,7 +17,7 @@ module CarrierWave
|
|
17
17
|
#
|
18
18
|
# By default, store!() uses copy_to(), which operates by copying the file
|
19
19
|
# from the cache to the store, then deleting the file from the cache.
|
20
|
-
# If move_to_store() is
|
20
|
+
# If move_to_store() is overridden to return true, then store!() uses move_to(),
|
21
21
|
# which simply moves the file from cache to store. Useful for large files.
|
22
22
|
#
|
23
23
|
# === Parameters
|
@@ -109,10 +109,11 @@ module CarrierWave
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def clean_cache!(seconds)
|
112
|
-
Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'),
|
113
|
-
# generate_cache_id returns key
|
114
|
-
|
115
|
-
|
112
|
+
Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), uploader.root)).each do |dir|
|
113
|
+
# generate_cache_id returns key formatted TIMEINT-PID(-COUNTER)-RND
|
114
|
+
matched = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first
|
115
|
+
next unless matched
|
116
|
+
time = Time.at(matched[0].to_i)
|
116
117
|
if time < (Time.now.utc - seconds)
|
117
118
|
FileUtils.rm_rf(dir)
|
118
119
|
end
|
@@ -30,7 +30,7 @@ module CarrierWave
|
|
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:
|
@@ -146,9 +146,10 @@ module CarrierWave
|
|
146
146
|
:key => uploader.fog_directory,
|
147
147
|
:public => uploader.fog_public
|
148
148
|
).files.all(:prefix => uploader.cache_dir).each do |file|
|
149
|
-
# generate_cache_id returns key
|
150
|
-
|
151
|
-
|
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)
|
152
153
|
file.destroy if time < (Time.now.utc - seconds)
|
153
154
|
end
|
154
155
|
end
|
@@ -161,9 +162,10 @@ module CarrierWave
|
|
161
162
|
end
|
162
163
|
|
163
164
|
class File
|
164
|
-
DEFAULT_S3_REGION = 'us-east-1'
|
165
|
+
DEFAULT_S3_REGION = 'us-east-1'.freeze
|
165
166
|
|
166
167
|
include CarrierWave::Utilities::Uri
|
168
|
+
include CarrierWave::Utilities::FileName
|
167
169
|
|
168
170
|
##
|
169
171
|
# Current local path to file
|
@@ -196,27 +198,27 @@ module CarrierWave
|
|
196
198
|
# [NilClass] no authenticated url available
|
197
199
|
#
|
198
200
|
def authenticated_url(options = {})
|
199
|
-
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(
|
201
|
+
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(fog_provider)
|
200
202
|
# avoid a get by using local references
|
201
203
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
202
204
|
local_file = local_directory.files.new(:key => path)
|
203
205
|
expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
|
204
|
-
case
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
else
|
210
|
-
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
211
|
-
local_file.url(expire_at)
|
212
|
-
end
|
213
|
-
when 'Rackspace', 'OpenStack'
|
214
|
-
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
|
215
|
-
when 'Aliyun'
|
216
|
-
expire_at = expire_at - Time.now
|
217
|
-
local_file.url(expire_at)
|
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)
|
218
211
|
else
|
212
|
+
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
219
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)
|
220
|
+
else
|
221
|
+
local_file.url(expire_at)
|
220
222
|
end
|
221
223
|
end
|
222
224
|
end
|
@@ -257,18 +259,6 @@ module CarrierWave
|
|
257
259
|
end
|
258
260
|
end
|
259
261
|
|
260
|
-
##
|
261
|
-
# Return extension of file
|
262
|
-
#
|
263
|
-
# === Returns
|
264
|
-
#
|
265
|
-
# [String] extension of file or nil if the file has no extension
|
266
|
-
#
|
267
|
-
def extension
|
268
|
-
path_elements = path.split('.')
|
269
|
-
path_elements.last if path_elements.size > 1
|
270
|
-
end
|
271
|
-
|
272
262
|
##
|
273
263
|
# deprecated: All attributes from file (includes headers)
|
274
264
|
#
|
@@ -295,16 +285,16 @@ module CarrierWave
|
|
295
285
|
#
|
296
286
|
# [String] contents of file
|
297
287
|
def read
|
298
|
-
file_body = file
|
288
|
+
file_body = file&.body
|
299
289
|
|
300
290
|
return if file_body.nil?
|
301
291
|
return file_body unless file_body.is_a?(::File)
|
302
292
|
|
303
|
-
# Fog::Storage::XXX::File#body could return the source file which was
|
304
|
-
read_source_file
|
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)
|
305
295
|
|
306
296
|
# If the source file doesn't exist, the remote content is read
|
307
|
-
@file = nil
|
297
|
+
@file = nil
|
308
298
|
file.body
|
309
299
|
end
|
310
300
|
|
@@ -342,7 +332,7 @@ module CarrierWave
|
|
342
332
|
fog_file = new_file.to_file
|
343
333
|
@content_type ||= new_file.content_type
|
344
334
|
@file = directory.files.create({
|
345
|
-
:body => fog_file
|
335
|
+
:body => fog_file || new_file.read,
|
346
336
|
:content_type => @content_type,
|
347
337
|
:key => path,
|
348
338
|
:public => @uploader.fog_public
|
@@ -363,7 +353,7 @@ module CarrierWave
|
|
363
353
|
#
|
364
354
|
def public_url
|
365
355
|
encoded_path = encode_path(path)
|
366
|
-
if host = @uploader.asset_host
|
356
|
+
if (host = @uploader.asset_host)
|
367
357
|
if host.respond_to? :call
|
368
358
|
"#{host.call(self)}/#{encoded_path}"
|
369
359
|
else
|
@@ -380,21 +370,22 @@ module CarrierWave
|
|
380
370
|
protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
|
381
371
|
|
382
372
|
subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
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}"
|
389
387
|
else # directory is not a valid subdomain, so use path style for access
|
390
|
-
|
391
|
-
host = case region
|
392
|
-
when DEFAULT_S3_REGION, ''
|
393
|
-
's3.amazonaws.com'
|
394
|
-
else
|
395
|
-
"s3.#{region}.amazonaws.com"
|
396
|
-
end
|
397
|
-
"#{protocol}://#{host}/#{@uploader.fog_directory}/#{encoded_path}"
|
388
|
+
"#{protocol}://#{regional_host}/#{@uploader.fog_directory}/#{encoded_path}"
|
398
389
|
end
|
399
390
|
end
|
400
391
|
when 'Google'
|
@@ -408,7 +399,7 @@ module CarrierWave
|
|
408
399
|
end
|
409
400
|
|
410
401
|
##
|
411
|
-
# Return url to file, if
|
402
|
+
# Return url to file, if available
|
412
403
|
#
|
413
404
|
# === Returns
|
414
405
|
#
|
@@ -434,7 +425,7 @@ module CarrierWave
|
|
434
425
|
# [NilClass] no file name available
|
435
426
|
#
|
436
427
|
def filename(options = {})
|
437
|
-
return unless file_url = url(options)
|
428
|
+
return unless (file_url = url(options))
|
438
429
|
CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
|
439
430
|
end
|
440
431
|
|
@@ -450,10 +441,29 @@ module CarrierWave
|
|
450
441
|
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
451
442
|
#
|
452
443
|
def copy_to(new_path)
|
453
|
-
|
444
|
+
file.copy(@uploader.fog_directory, new_path, copy_options)
|
454
445
|
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
455
446
|
end
|
456
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
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
457
467
|
private
|
458
468
|
|
459
469
|
##
|
@@ -475,12 +485,10 @@ module CarrierWave
|
|
475
485
|
# [Fog::#{provider}::Directory] containing directory
|
476
486
|
#
|
477
487
|
def directory
|
478
|
-
@directory ||=
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
)
|
483
|
-
end
|
488
|
+
@directory ||= connection.directories.new(
|
489
|
+
:key => @uploader.fog_directory,
|
490
|
+
:public => @uploader.fog_public
|
491
|
+
)
|
484
492
|
end
|
485
493
|
|
486
494
|
##
|
@@ -497,14 +505,15 @@ module CarrierWave
|
|
497
505
|
def copy_options
|
498
506
|
options = {}
|
499
507
|
options.merge!(acl_header) if acl_header.present?
|
500
|
-
options['Content-Type'] ||= content_type if content_type
|
508
|
+
options[fog_provider == "Google" ? :content_type : 'Content-Type'] ||= content_type if content_type
|
501
509
|
options.merge(@uploader.fog_attributes)
|
502
510
|
end
|
503
511
|
|
504
512
|
def acl_header
|
505
|
-
|
513
|
+
case fog_provider
|
514
|
+
when 'AWS'
|
506
515
|
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
507
|
-
|
516
|
+
when "Google"
|
508
517
|
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
509
518
|
else
|
510
519
|
{}
|
@@ -515,14 +524,14 @@ module CarrierWave
|
|
515
524
|
@uploader.fog_credentials[:provider].to_s
|
516
525
|
end
|
517
526
|
|
518
|
-
def read_source_file
|
519
|
-
|
527
|
+
def read_source_file
|
528
|
+
source_file = to_file
|
529
|
+
return unless source_file
|
520
530
|
|
521
531
|
begin
|
522
|
-
|
523
|
-
file_body.read
|
532
|
+
source_file.read
|
524
533
|
ensure
|
525
|
-
|
534
|
+
source_file.close
|
526
535
|
end
|
527
536
|
end
|
528
537
|
|
@@ -45,11 +45,11 @@ module CarrierWave
|
|
45
45
|
def matches?(actual)
|
46
46
|
@actual = actual
|
47
47
|
# Satisfy expectation here. Return false or raise an error if it's not met.
|
48
|
-
(File.stat(@actual.path).mode &
|
48
|
+
(File.stat(@actual.path).mode & 0o777) == @expected
|
49
49
|
end
|
50
50
|
|
51
51
|
def failure_message
|
52
|
-
"expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode &
|
52
|
+
"expected #{@actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
|
53
53
|
end
|
54
54
|
|
55
55
|
def failure_message_when_negated
|
@@ -76,11 +76,11 @@ module CarrierWave
|
|
76
76
|
def matches?(actual)
|
77
77
|
@actual = actual
|
78
78
|
# Satisfy expectation here. Return false or raise an error if it's not met.
|
79
|
-
(File.stat(File.dirname
|
79
|
+
(File.stat(File.dirname(@actual.path)).mode & 0o777) == @expected
|
80
80
|
end
|
81
81
|
|
82
82
|
def failure_message
|
83
|
-
"expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode &
|
83
|
+
"expected #{File.dirname @actual.current_path.inspect} to have permissions #{@expected.to_s(8)}, but they were #{(File.stat(@actual.path).mode & 0o777).to_s(8)}"
|
84
84
|
end
|
85
85
|
|
86
86
|
def failure_message_when_negated
|
@@ -341,9 +341,11 @@ module CarrierWave
|
|
341
341
|
begin
|
342
342
|
require 'rmagick'
|
343
343
|
rescue LoadError
|
344
|
-
|
345
|
-
|
346
|
-
|
344
|
+
begin
|
345
|
+
require 'RMagick'
|
346
|
+
rescue LoadError
|
347
|
+
puts "WARNING: Failed to require rmagick, image processing may fail!"
|
348
|
+
end
|
347
349
|
end
|
348
350
|
end
|
349
351
|
MagickWrapper.new(filename)
|
@@ -353,6 +355,7 @@ module CarrierWave
|
|
353
355
|
|
354
356
|
class MagickWrapper # :nodoc:
|
355
357
|
attr_reader :image
|
358
|
+
|
356
359
|
def width
|
357
360
|
image.columns
|
358
361
|
end
|
@@ -372,6 +375,7 @@ module CarrierWave
|
|
372
375
|
|
373
376
|
class MiniMagickWrapper # :nodoc:
|
374
377
|
attr_reader :image
|
378
|
+
|
375
379
|
def width
|
376
380
|
image[:width]
|
377
381
|
end
|