carrierwave 1.3.2 → 3.0.2
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 +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 +48 -61
- data/lib/carrierwave/mounter.rb +167 -77
- data/lib/carrierwave/orm/activerecord.rb +15 -55
- 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 +153 -105
- 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} +2 -2
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +100 -33
- 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
|
+
@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
|