carrierwave 1.3.2 → 3.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +235 -91
- data/lib/carrierwave/compatibility/paperclip.rb +4 -2
- data/lib/carrierwave/downloader/base.rb +101 -0
- data/lib/carrierwave/downloader/remote_file.rb +68 -0
- data/lib/carrierwave/locale/en.yml +9 -6
- data/lib/carrierwave/mount.rb +53 -61
- data/lib/carrierwave/mounter.rb +167 -77
- data/lib/carrierwave/orm/activerecord.rb +23 -58
- data/lib/carrierwave/processing/mini_magick.rb +108 -123
- data/lib/carrierwave/processing/rmagick.rb +11 -15
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +60 -66
- data/lib/carrierwave/storage/abstract.rb +5 -5
- data/lib/carrierwave/storage/file.rb +6 -5
- data/lib/carrierwave/storage/fog.rb +101 -62
- data/lib/carrierwave/storage.rb +1 -0
- data/lib/carrierwave/test/matchers.rb +11 -7
- data/lib/carrierwave/uploader/cache.rb +40 -24
- data/lib/carrierwave/uploader/callbacks.rb +1 -1
- data/lib/carrierwave/uploader/configuration.rb +38 -19
- data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
- data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/download.rb +2 -123
- 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 +2 -2
- data/lib/carrierwave/uploader/mountable.rb +6 -0
- data/lib/carrierwave/uploader/processing.rb +42 -7
- data/lib/carrierwave/uploader/proxy.rb +17 -4
- data/lib/carrierwave/uploader/serialization.rb +1 -1
- data/lib/carrierwave/uploader/store.rb +47 -7
- data/lib/carrierwave/uploader/url.rb +7 -4
- data/lib/carrierwave/uploader/versions.rb +157 -109
- data/lib/carrierwave/uploader.rb +10 -17
- 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 +7 -9
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +13 -17
- data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +3 -3
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +103 -36
- 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 -51
- data/lib/carrierwave/uploader/extension_whitelist.rb +0 -52
@@ -1,12 +1,6 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'active_support/core_ext/string/multibyte'
|
3
|
-
|
4
|
-
begin
|
5
|
-
# Use mime/types/columnar if available, for reduced memory usage
|
6
|
-
require 'mime/types/columnar'
|
7
|
-
rescue LoadError
|
8
|
-
require 'mime/types'
|
9
|
-
end
|
3
|
+
require 'marcel'
|
10
4
|
|
11
5
|
module CarrierWave
|
12
6
|
|
@@ -19,6 +13,7 @@ module CarrierWave
|
|
19
13
|
# It's probably needlessly comprehensive and complex. Help is appreciated.
|
20
14
|
#
|
21
15
|
class SanitizedFile
|
16
|
+
include CarrierWave::Utilities::FileName
|
22
17
|
|
23
18
|
attr_reader :file
|
24
19
|
|
@@ -32,7 +27,7 @@ module CarrierWave
|
|
32
27
|
|
33
28
|
def initialize(file)
|
34
29
|
self.file = file
|
35
|
-
@content = nil
|
30
|
+
@content = @content_type = nil
|
36
31
|
end
|
37
32
|
|
38
33
|
##
|
@@ -44,7 +39,7 @@ module CarrierWave
|
|
44
39
|
#
|
45
40
|
def original_filename
|
46
41
|
return @original_filename if @original_filename
|
47
|
-
if @file
|
42
|
+
if @file && @file.respond_to?(:original_filename)
|
48
43
|
@file.original_filename
|
49
44
|
elsif path
|
50
45
|
File.basename(path)
|
@@ -64,29 +59,6 @@ module CarrierWave
|
|
64
59
|
|
65
60
|
alias_method :identifier, :filename
|
66
61
|
|
67
|
-
##
|
68
|
-
# Returns the part of the filename before the extension. So if a file is called 'test.jpeg'
|
69
|
-
# this would return 'test'
|
70
|
-
#
|
71
|
-
# === Returns
|
72
|
-
#
|
73
|
-
# [String] the first part of the filename
|
74
|
-
#
|
75
|
-
def basename
|
76
|
-
split_extension(filename)[0] if filename
|
77
|
-
end
|
78
|
-
|
79
|
-
##
|
80
|
-
# Returns the file extension
|
81
|
-
#
|
82
|
-
# === Returns
|
83
|
-
#
|
84
|
-
# [String] the extension
|
85
|
-
#
|
86
|
-
def extension
|
87
|
-
split_extension(filename)[1] if filename
|
88
|
-
end
|
89
|
-
|
90
62
|
##
|
91
63
|
# Returns the file's size.
|
92
64
|
#
|
@@ -137,7 +109,7 @@ module CarrierWave
|
|
137
109
|
# [Boolean] whether the file is valid and has a non-zero size
|
138
110
|
#
|
139
111
|
def empty?
|
140
|
-
@file.nil? || self.size.nil? || (self.size.zero? && !
|
112
|
+
@file.nil? || self.size.nil? || (self.size.zero? && !self.exists?)
|
141
113
|
end
|
142
114
|
|
143
115
|
##
|
@@ -156,15 +128,21 @@ module CarrierWave
|
|
156
128
|
#
|
157
129
|
# [String] contents of the file
|
158
130
|
#
|
159
|
-
def read
|
131
|
+
def read(*args)
|
160
132
|
if @content
|
161
|
-
|
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
|
162
140
|
elsif is_path?
|
163
|
-
File.open(@file, "rb") {|file| file.read}
|
141
|
+
File.open(@file, "rb") {|file| file.read(*args)}
|
164
142
|
else
|
165
143
|
@file.try(:rewind)
|
166
|
-
@content = @file.read
|
167
|
-
@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?)
|
168
146
|
@content
|
169
147
|
end
|
170
148
|
end
|
@@ -185,13 +163,10 @@ module CarrierWave
|
|
185
163
|
mkdir!(new_path, directory_permissions)
|
186
164
|
move!(new_path)
|
187
165
|
chmod!(new_path, permissions)
|
188
|
-
|
189
|
-
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
|
190
|
-
else
|
191
|
-
self.file = {:tempfile => new_path, :content_type => content_type}
|
192
|
-
end
|
166
|
+
self.file = {tempfile: new_path, filename: keep_filename ? original_filename : nil, content_type: declared_content_type}
|
193
167
|
self
|
194
168
|
end
|
169
|
+
|
195
170
|
##
|
196
171
|
# Helper to move file to new path.
|
197
172
|
#
|
@@ -223,7 +198,7 @@ module CarrierWave
|
|
223
198
|
mkdir!(new_path, directory_permissions)
|
224
199
|
copy!(new_path)
|
225
200
|
chmod!(new_path, permissions)
|
226
|
-
self.class.new({:
|
201
|
+
self.class.new({tempfile: new_path, content_type: declared_content_type})
|
227
202
|
end
|
228
203
|
|
229
204
|
##
|
@@ -264,12 +239,11 @@ module CarrierWave
|
|
264
239
|
# [String] the content type of the file
|
265
240
|
#
|
266
241
|
def content_type
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
end
|
242
|
+
@content_type ||=
|
243
|
+
identified_content_type ||
|
244
|
+
declared_content_type ||
|
245
|
+
guessed_safe_content_type ||
|
246
|
+
Marcel::MimeType::BINARY
|
273
247
|
end
|
274
248
|
|
275
249
|
##
|
@@ -300,11 +274,11 @@ module CarrierWave
|
|
300
274
|
if file.is_a?(Hash)
|
301
275
|
@file = file["tempfile"] || file[:tempfile]
|
302
276
|
@original_filename = file["filename"] || file[:filename]
|
303
|
-
@
|
277
|
+
@declared_content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
|
304
278
|
else
|
305
279
|
@file = file
|
306
280
|
@original_filename = nil
|
307
|
-
@
|
281
|
+
@declared_content_type = nil
|
308
282
|
end
|
309
283
|
end
|
310
284
|
|
@@ -321,28 +295,48 @@ module CarrierWave
|
|
321
295
|
|
322
296
|
# Sanitize the filename, to prevent hacking
|
323
297
|
def sanitize(name)
|
298
|
+
name = name.scrub
|
324
299
|
name = name.tr("\\", "/") # work-around for IE
|
325
300
|
name = File.basename(name)
|
326
|
-
name = name.gsub(sanitize_regexp,"_")
|
301
|
+
name = name.gsub(sanitize_regexp, "_")
|
327
302
|
name = "_#{name}" if name =~ /\A\.+\z/
|
328
|
-
name = "unnamed" if name.size
|
329
|
-
|
303
|
+
name = "unnamed" if name.size.zero?
|
304
|
+
name.mb_chars.to_s
|
330
305
|
end
|
331
306
|
|
332
|
-
def
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
/\A(.+)\.([^\.]+)\z/ # matches "something.jpg"
|
337
|
-
]
|
338
|
-
|
339
|
-
extension_matchers.each do |regexp|
|
340
|
-
if filename =~ regexp
|
341
|
-
return $1, $2
|
307
|
+
def declared_content_type
|
308
|
+
@declared_content_type ||
|
309
|
+
if @file.respond_to?(:content_type) && @file.content_type
|
310
|
+
Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp)
|
342
311
|
end
|
312
|
+
end
|
313
|
+
|
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
|
317
|
+
|
318
|
+
type = Marcel::Magic.by_path(original_filename).to_s
|
319
|
+
type if type.start_with?('text/') || type.start_with?('application/json')
|
320
|
+
end
|
321
|
+
|
322
|
+
def identified_content_type
|
323
|
+
with_io do |io|
|
324
|
+
Marcel::Magic.by_magic(io).try(:type)
|
343
325
|
end
|
344
|
-
|
326
|
+
rescue Errno::ENOENT
|
327
|
+
nil
|
345
328
|
end
|
346
329
|
|
330
|
+
def with_io(&block)
|
331
|
+
if file.is_a?(IO)
|
332
|
+
begin
|
333
|
+
yield file
|
334
|
+
ensure
|
335
|
+
file.try(:rewind)
|
336
|
+
end
|
337
|
+
elsif path
|
338
|
+
File.open(path, &block)
|
339
|
+
end
|
340
|
+
end
|
347
341
|
end # SanitizedFile
|
348
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:
|
@@ -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
|
##
|
@@ -138,9 +146,10 @@ module CarrierWave
|
|
138
146
|
:key => uploader.fog_directory,
|
139
147
|
:public => uploader.fog_public
|
140
148
|
).files.all(:prefix => uploader.cache_dir).each do |file|
|
141
|
-
# generate_cache_id returns key
|
142
|
-
|
143
|
-
|
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)
|
144
153
|
file.destroy if time < (Time.now.utc - seconds)
|
145
154
|
end
|
146
155
|
end
|
@@ -153,7 +162,10 @@ module CarrierWave
|
|
153
162
|
end
|
154
163
|
|
155
164
|
class File
|
165
|
+
DEFAULT_S3_REGION = 'us-east-1'.freeze
|
166
|
+
|
156
167
|
include CarrierWave::Utilities::Uri
|
168
|
+
include CarrierWave::Utilities::FileName
|
157
169
|
|
158
170
|
##
|
159
171
|
# Current local path to file
|
@@ -177,7 +189,7 @@ module CarrierWave
|
|
177
189
|
|
178
190
|
##
|
179
191
|
# Return a temporary authenticated url to a private file, if available
|
180
|
-
# Only supported for AWS, Rackspace, Google and
|
192
|
+
# Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
|
181
193
|
#
|
182
194
|
# === Returns
|
183
195
|
#
|
@@ -186,24 +198,27 @@ module CarrierWave
|
|
186
198
|
# [NilClass] no authenticated url available
|
187
199
|
#
|
188
200
|
def authenticated_url(options = {})
|
189
|
-
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM'].include?(
|
201
|
+
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(fog_provider)
|
190
202
|
# avoid a get by using local references
|
191
203
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
192
204
|
local_file = local_directory.files.new(:key => path)
|
193
|
-
expire_at = ::Fog::Time.now
|
194
|
-
case
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
else
|
200
|
-
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
201
|
-
local_file.url(expire_at)
|
202
|
-
end
|
203
|
-
when 'Rackspace', 'OpenStack'
|
204
|
-
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
|
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)
|
205
211
|
else
|
212
|
+
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
206
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)
|
207
222
|
end
|
208
223
|
end
|
209
224
|
end
|
@@ -216,7 +231,7 @@ module CarrierWave
|
|
216
231
|
# [String] value of content-type
|
217
232
|
#
|
218
233
|
def content_type
|
219
|
-
@content_type ||
|
234
|
+
@content_type || file.try(:content_type)
|
220
235
|
end
|
221
236
|
|
222
237
|
##
|
@@ -239,19 +254,9 @@ module CarrierWave
|
|
239
254
|
#
|
240
255
|
def delete
|
241
256
|
# avoid a get by just using local reference
|
242
|
-
directory.files.new(:key => path).destroy
|
243
|
-
|
244
|
-
|
245
|
-
##
|
246
|
-
# Return extension of file
|
247
|
-
#
|
248
|
-
# === Returns
|
249
|
-
#
|
250
|
-
# [String] extension of file or nil if the file has no extension
|
251
|
-
#
|
252
|
-
def extension
|
253
|
-
path_elements = path.split('.')
|
254
|
-
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
|
255
260
|
end
|
256
261
|
|
257
262
|
##
|
@@ -280,16 +285,16 @@ module CarrierWave
|
|
280
285
|
#
|
281
286
|
# [String] contents of file
|
282
287
|
def read
|
283
|
-
file_body = file
|
288
|
+
file_body = file&.body
|
284
289
|
|
285
290
|
return if file_body.nil?
|
286
291
|
return file_body unless file_body.is_a?(::File)
|
287
292
|
|
288
|
-
# Fog::Storage::XXX::File#body could return the source file which was
|
289
|
-
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)
|
290
295
|
|
291
296
|
# If the source file doesn't exist, the remote content is read
|
292
|
-
@file = nil
|
297
|
+
@file = nil
|
293
298
|
file.body
|
294
299
|
end
|
295
300
|
|
@@ -327,7 +332,7 @@ module CarrierWave
|
|
327
332
|
fog_file = new_file.to_file
|
328
333
|
@content_type ||= new_file.content_type
|
329
334
|
@file = directory.files.create({
|
330
|
-
:body => fog_file
|
335
|
+
:body => fog_file || new_file.read,
|
331
336
|
:content_type => @content_type,
|
332
337
|
:key => path,
|
333
338
|
:public => @uploader.fog_public
|
@@ -348,7 +353,7 @@ module CarrierWave
|
|
348
353
|
#
|
349
354
|
def public_url
|
350
355
|
encoded_path = encode_path(path)
|
351
|
-
if host = @uploader.asset_host
|
356
|
+
if (host = @uploader.asset_host)
|
352
357
|
if host.respond_to? :call
|
353
358
|
"#{host.call(self)}/#{encoded_path}"
|
354
359
|
else
|
@@ -365,15 +370,22 @@ module CarrierWave
|
|
365
370
|
protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
|
366
371
|
|
367
372
|
subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
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}"
|
377
389
|
end
|
378
390
|
end
|
379
391
|
when 'Google'
|
@@ -387,7 +399,7 @@ module CarrierWave
|
|
387
399
|
end
|
388
400
|
|
389
401
|
##
|
390
|
-
# Return url to file, if
|
402
|
+
# Return url to file, if available
|
391
403
|
#
|
392
404
|
# === Returns
|
393
405
|
#
|
@@ -413,7 +425,7 @@ module CarrierWave
|
|
413
425
|
# [NilClass] no file name available
|
414
426
|
#
|
415
427
|
def filename(options = {})
|
416
|
-
return unless file_url = url(options)
|
428
|
+
return unless (file_url = url(options))
|
417
429
|
CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
|
418
430
|
end
|
419
431
|
|
@@ -429,10 +441,29 @@ module CarrierWave
|
|
429
441
|
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
430
442
|
#
|
431
443
|
def copy_to(new_path)
|
432
|
-
|
444
|
+
file.copy(@uploader.fog_directory, new_path, copy_options)
|
433
445
|
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
434
446
|
end
|
435
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
|
+
|
436
467
|
private
|
437
468
|
|
438
469
|
##
|
@@ -454,12 +485,10 @@ module CarrierWave
|
|
454
485
|
# [Fog::#{provider}::Directory] containing directory
|
455
486
|
#
|
456
487
|
def directory
|
457
|
-
@directory ||=
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
)
|
462
|
-
end
|
488
|
+
@directory ||= connection.directories.new(
|
489
|
+
:key => @uploader.fog_directory,
|
490
|
+
:public => @uploader.fog_public
|
491
|
+
)
|
463
492
|
end
|
464
493
|
|
465
494
|
##
|
@@ -473,9 +502,19 @@ module CarrierWave
|
|
473
502
|
@file ||= directory.files.head(path)
|
474
503
|
end
|
475
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
|
+
|
476
512
|
def acl_header
|
477
|
-
|
513
|
+
case fog_provider
|
514
|
+
when 'AWS'
|
478
515
|
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
516
|
+
when "Google"
|
517
|
+
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
479
518
|
else
|
480
519
|
{}
|
481
520
|
end
|
@@ -485,14 +524,14 @@ module CarrierWave
|
|
485
524
|
@uploader.fog_credentials[:provider].to_s
|
486
525
|
end
|
487
526
|
|
488
|
-
def read_source_file
|
489
|
-
|
527
|
+
def read_source_file
|
528
|
+
source_file = to_file
|
529
|
+
return unless source_file
|
490
530
|
|
491
531
|
begin
|
492
|
-
|
493
|
-
file_body.read
|
532
|
+
source_file.read
|
494
533
|
ensure
|
495
|
-
|
534
|
+
source_file.close
|
496
535
|
end
|
497
536
|
end
|
498
537
|
|
data/lib/carrierwave/storage.rb
CHANGED
@@ -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
|