carrierwave 0.11.2 → 2.1.0
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 +294 -124
- data/lib/carrierwave.rb +34 -8
- data/lib/carrierwave/compatibility/paperclip.rb +0 -2
- data/lib/carrierwave/downloader/base.rb +50 -0
- data/lib/carrierwave/downloader/remote_file.rb +44 -0
- data/lib/carrierwave/error.rb +1 -0
- data/lib/carrierwave/locale/en.yml +7 -4
- data/lib/carrierwave/mount.rb +229 -180
- data/lib/carrierwave/mounter.rb +188 -0
- data/lib/carrierwave/orm/activerecord.rb +59 -24
- data/lib/carrierwave/processing.rb +0 -1
- data/lib/carrierwave/processing/mini_magick.rb +137 -83
- data/lib/carrierwave/processing/rmagick.rb +54 -5
- data/lib/carrierwave/sanitized_file.rb +50 -30
- data/lib/carrierwave/storage.rb +1 -9
- data/lib/carrierwave/storage/abstract.rb +15 -2
- data/lib/carrierwave/storage/file.rb +69 -2
- data/lib/carrierwave/storage/fog.rb +177 -37
- data/lib/carrierwave/test/matchers.rb +77 -12
- data/lib/carrierwave/uploader.rb +2 -2
- data/lib/carrierwave/uploader/cache.rb +60 -38
- data/lib/carrierwave/uploader/callbacks.rb +0 -2
- data/lib/carrierwave/uploader/configuration.rb +71 -13
- data/lib/carrierwave/uploader/content_type_whitelist.rb +1 -1
- data/lib/carrierwave/uploader/default_url.rb +3 -5
- data/lib/carrierwave/uploader/download.rb +4 -74
- data/lib/carrierwave/uploader/extension_blacklist.rb +14 -10
- data/lib/carrierwave/uploader/extension_whitelist.rb +13 -10
- data/lib/carrierwave/uploader/file_size.rb +43 -0
- data/lib/carrierwave/uploader/mountable.rb +13 -8
- data/lib/carrierwave/uploader/processing.rb +10 -10
- data/lib/carrierwave/uploader/proxy.rb +6 -8
- data/lib/carrierwave/uploader/remove.rb +0 -2
- data/lib/carrierwave/uploader/serialization.rb +2 -4
- data/lib/carrierwave/uploader/store.rb +17 -24
- data/lib/carrierwave/uploader/url.rb +3 -5
- data/lib/carrierwave/uploader/versions.rb +123 -93
- data/lib/carrierwave/utilities.rb +0 -3
- data/lib/carrierwave/utilities/uri.rb +5 -6
- data/lib/carrierwave/validations/active_model.rb +3 -5
- data/lib/carrierwave/version.rb +1 -1
- data/lib/generators/templates/uploader.rb +4 -8
- metadata +80 -65
- 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/utilities/deprecation.rb +0 -18
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
|
5
3
|
##
|
@@ -69,6 +67,13 @@ module CarrierWave
|
|
69
67
|
e.message << " (You may need to install the rmagick gem)"
|
70
68
|
raise e
|
71
69
|
end
|
70
|
+
|
71
|
+
prepend Module.new {
|
72
|
+
def initialize(*)
|
73
|
+
super
|
74
|
+
@format = nil
|
75
|
+
end
|
76
|
+
}
|
72
77
|
end
|
73
78
|
|
74
79
|
module ClassMethods
|
@@ -135,6 +140,8 @@ module CarrierWave
|
|
135
140
|
# [Magick::Image] additional manipulations to perform
|
136
141
|
#
|
137
142
|
def resize_to_limit(width, height)
|
143
|
+
width = dimension_from width
|
144
|
+
height = dimension_from height
|
138
145
|
manipulate! do |img|
|
139
146
|
geometry = Magick::Geometry.new(width, height, 0, 0, Magick::GreaterGeometry)
|
140
147
|
new_img = img.change_geometry(geometry) do |new_width, new_height|
|
@@ -164,6 +171,8 @@ module CarrierWave
|
|
164
171
|
# [Magick::Image] additional manipulations to perform
|
165
172
|
#
|
166
173
|
def resize_to_fit(width, height)
|
174
|
+
width = dimension_from width
|
175
|
+
height = dimension_from height
|
167
176
|
manipulate! do |img|
|
168
177
|
img.resize_to_fit!(width, height)
|
169
178
|
img = yield(img) if block_given?
|
@@ -188,6 +197,8 @@ module CarrierWave
|
|
188
197
|
# [Magick::Image] additional manipulations to perform
|
189
198
|
#
|
190
199
|
def resize_to_fill(width, height, gravity=::Magick::CenterGravity)
|
200
|
+
width = dimension_from width
|
201
|
+
height = dimension_from height
|
191
202
|
manipulate! do |img|
|
192
203
|
img.crop_resized!(width, height, gravity)
|
193
204
|
img = yield(img) if block_given?
|
@@ -213,6 +224,8 @@ module CarrierWave
|
|
213
224
|
# [Magick::Image] additional manipulations to perform
|
214
225
|
#
|
215
226
|
def resize_and_pad(width, height, background=:transparent, gravity=::Magick::CenterGravity)
|
227
|
+
width = dimension_from width
|
228
|
+
height = dimension_from height
|
216
229
|
manipulate! do |img|
|
217
230
|
img.resize_to_fit!(width, height)
|
218
231
|
new_img = ::Magick::Image.new(width, height) { self.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
|
@@ -251,6 +264,28 @@ module CarrierWave
|
|
251
264
|
end
|
252
265
|
end
|
253
266
|
|
267
|
+
##
|
268
|
+
# Returns the width of the image.
|
269
|
+
#
|
270
|
+
# === Returns
|
271
|
+
#
|
272
|
+
# [Integer] the image's width in pixels
|
273
|
+
#
|
274
|
+
def width
|
275
|
+
rmagick_image.columns
|
276
|
+
end
|
277
|
+
|
278
|
+
##
|
279
|
+
# Returns the height of the image.
|
280
|
+
#
|
281
|
+
# === Returns
|
282
|
+
#
|
283
|
+
# [Integer] the image's height in pixels
|
284
|
+
#
|
285
|
+
def height
|
286
|
+
rmagick_image.rows
|
287
|
+
end
|
288
|
+
|
254
289
|
##
|
255
290
|
# Manipulate the image with RMagick. This method will load up an image
|
256
291
|
# and then pass each of its frames to the supplied block. It will then
|
@@ -318,20 +353,25 @@ module CarrierWave
|
|
318
353
|
frames = ::Magick::ImageList.new
|
319
354
|
|
320
355
|
image.each_with_index do |frame, index|
|
321
|
-
frame = yield
|
356
|
+
frame = yield(*[frame, index, options].take(block.arity)) if block_given?
|
322
357
|
frames << frame if frame
|
323
358
|
end
|
324
359
|
frames.append(true) if block_given?
|
325
360
|
|
326
361
|
write_block = create_info_block(options[:write])
|
362
|
+
|
327
363
|
if options[:format] || @format
|
328
364
|
frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
|
365
|
+
move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
|
366
|
+
file.content_type = ::MiniMime.lookup_by_filename(move_to).content_type
|
367
|
+
file.move_to(move_to, permissions, directory_permissions)
|
329
368
|
else
|
330
369
|
frames.write(current_path, &write_block)
|
331
370
|
end
|
371
|
+
|
332
372
|
destroy_image(frames)
|
333
373
|
rescue ::Magick::ImageMagickError => e
|
334
|
-
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e
|
374
|
+
raise CarrierWave::ProcessingError, I18n.translate(:"errors.messages.rmagick_processing_error", :e => e)
|
335
375
|
end
|
336
376
|
|
337
377
|
private
|
@@ -344,7 +384,16 @@ module CarrierWave
|
|
344
384
|
end
|
345
385
|
|
346
386
|
def destroy_image(image)
|
347
|
-
image.
|
387
|
+
image.try(:destroy!)
|
388
|
+
end
|
389
|
+
|
390
|
+
def dimension_from(value)
|
391
|
+
return value unless value.instance_of?(Proc)
|
392
|
+
value.arity >= 1 ? value.call(self) : value.call
|
393
|
+
end
|
394
|
+
|
395
|
+
def rmagick_image
|
396
|
+
::Magick::Image.from_blob(self.read).first
|
348
397
|
end
|
349
398
|
|
350
399
|
end # RMagick
|
@@ -1,8 +1,6 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'pathname'
|
4
2
|
require 'active_support/core_ext/string/multibyte'
|
5
|
-
require '
|
3
|
+
require 'mini_mime'
|
6
4
|
require 'mimemagic'
|
7
5
|
|
8
6
|
module CarrierWave
|
@@ -17,18 +15,19 @@ module CarrierWave
|
|
17
15
|
#
|
18
16
|
class SanitizedFile
|
19
17
|
|
20
|
-
|
18
|
+
attr_reader :file
|
21
19
|
|
22
20
|
class << self
|
23
21
|
attr_writer :sanitize_regexp
|
24
22
|
|
25
23
|
def sanitize_regexp
|
26
|
-
@sanitize_regexp ||= /[^
|
24
|
+
@sanitize_regexp ||= /[^[:word:]\.\-\+]/
|
27
25
|
end
|
28
26
|
end
|
29
27
|
|
30
28
|
def initialize(file)
|
31
29
|
self.file = file
|
30
|
+
@content = nil
|
32
31
|
end
|
33
32
|
|
34
33
|
##
|
@@ -110,12 +109,11 @@ module CarrierWave
|
|
110
109
|
# [String, nil] the path where the file is located.
|
111
110
|
#
|
112
111
|
def path
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
end
|
112
|
+
return if @file.blank?
|
113
|
+
if is_path?
|
114
|
+
File.expand_path(@file)
|
115
|
+
elsif @file.respond_to?(:path) && !@file.path.blank?
|
116
|
+
File.expand_path(@file.path)
|
119
117
|
end
|
120
118
|
end
|
121
119
|
|
@@ -143,8 +141,7 @@ module CarrierWave
|
|
143
141
|
# [Boolean] Whether the file exists
|
144
142
|
#
|
145
143
|
def exists?
|
146
|
-
|
147
|
-
return false
|
144
|
+
self.path.present? && File.exist?(self.path)
|
148
145
|
end
|
149
146
|
|
150
147
|
##
|
@@ -160,9 +157,9 @@ module CarrierWave
|
|
160
157
|
elsif is_path?
|
161
158
|
File.open(@file, "rb") {|file| file.read}
|
162
159
|
else
|
163
|
-
@file.
|
160
|
+
@file.try(:rewind)
|
164
161
|
@content = @file.read
|
165
|
-
@file.
|
162
|
+
@file.try(:close) unless @file.try(:closed?)
|
166
163
|
@content
|
167
164
|
end
|
168
165
|
end
|
@@ -176,19 +173,29 @@ module CarrierWave
|
|
176
173
|
# [permissions (Integer)] permissions to set on the file in its new location.
|
177
174
|
# [directory_permissions (Integer)] permissions to set on created directories.
|
178
175
|
#
|
179
|
-
def move_to(new_path, permissions=nil, directory_permissions=nil)
|
176
|
+
def move_to(new_path, permissions=nil, directory_permissions=nil, keep_filename=false)
|
180
177
|
return if self.empty?
|
181
178
|
new_path = File.expand_path(new_path)
|
182
179
|
|
183
180
|
mkdir!(new_path, directory_permissions)
|
181
|
+
move!(new_path)
|
182
|
+
chmod!(new_path, permissions)
|
183
|
+
if keep_filename
|
184
|
+
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
|
185
|
+
else
|
186
|
+
self.file = {:tempfile => new_path, :content_type => content_type}
|
187
|
+
end
|
188
|
+
self
|
189
|
+
end
|
190
|
+
##
|
191
|
+
# Helper to move file to new path.
|
192
|
+
#
|
193
|
+
def move!(new_path)
|
184
194
|
if exists?
|
185
|
-
FileUtils.mv(path, new_path) unless new_path
|
195
|
+
FileUtils.mv(path, new_path) unless File.identical?(new_path, path)
|
186
196
|
else
|
187
197
|
File.open(new_path, "wb") { |f| f.write(read) }
|
188
198
|
end
|
189
|
-
chmod!(new_path, permissions)
|
190
|
-
self.file = new_path
|
191
|
-
self
|
192
199
|
end
|
193
200
|
|
194
201
|
##
|
@@ -209,13 +216,20 @@ module CarrierWave
|
|
209
216
|
new_path = File.expand_path(new_path)
|
210
217
|
|
211
218
|
mkdir!(new_path, directory_permissions)
|
219
|
+
copy!(new_path)
|
220
|
+
chmod!(new_path, permissions)
|
221
|
+
self.class.new({:tempfile => new_path, :content_type => content_type})
|
222
|
+
end
|
223
|
+
|
224
|
+
##
|
225
|
+
# Helper to create copy of file in new path.
|
226
|
+
#
|
227
|
+
def copy!(new_path)
|
212
228
|
if exists?
|
213
229
|
FileUtils.cp(path, new_path) unless new_path == path
|
214
230
|
else
|
215
231
|
File.open(new_path, "wb") { |f| f.write(read) }
|
216
232
|
end
|
217
|
-
chmod!(new_path, permissions)
|
218
|
-
self.class.new({:tempfile => new_path, :content_type => content_type})
|
219
233
|
end
|
220
234
|
|
221
235
|
##
|
@@ -248,7 +262,7 @@ module CarrierWave
|
|
248
262
|
@content_type ||=
|
249
263
|
existing_content_type ||
|
250
264
|
mime_magic_content_type ||
|
251
|
-
|
265
|
+
mini_mime_content_type
|
252
266
|
end
|
253
267
|
|
254
268
|
##
|
@@ -279,7 +293,7 @@ module CarrierWave
|
|
279
293
|
if file.is_a?(Hash)
|
280
294
|
@file = file["tempfile"] || file[:tempfile]
|
281
295
|
@original_filename = file["filename"] || file[:filename]
|
282
|
-
@content_type = file["content_type"] || file[:content_type]
|
296
|
+
@content_type = file["content_type"] || file[:content_type] || file["type"] || file[:type]
|
283
297
|
else
|
284
298
|
@file = file
|
285
299
|
@original_filename = nil
|
@@ -291,7 +305,7 @@ module CarrierWave
|
|
291
305
|
def mkdir!(path, directory_permissions)
|
292
306
|
options = {}
|
293
307
|
options[:mode] = directory_permissions if directory_permissions
|
294
|
-
FileUtils.mkdir_p(File.dirname(path), options) unless File.
|
308
|
+
FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
|
295
309
|
end
|
296
310
|
|
297
311
|
def chmod!(path, permissions)
|
@@ -300,11 +314,11 @@ module CarrierWave
|
|
300
314
|
|
301
315
|
# Sanitize the filename, to prevent hacking
|
302
316
|
def sanitize(name)
|
303
|
-
name = name.
|
317
|
+
name = name.tr("\\", "/") # work-around for IE
|
304
318
|
name = File.basename(name)
|
305
319
|
name = name.gsub(sanitize_regexp,"_")
|
306
320
|
name = "_#{name}" if name =~ /\A\.+\z/
|
307
|
-
name = "unnamed" if name.size
|
321
|
+
name = "unnamed" if name.size.zero?
|
308
322
|
return name.mb_chars.to_s
|
309
323
|
end
|
310
324
|
|
@@ -315,13 +329,19 @@ module CarrierWave
|
|
315
329
|
end
|
316
330
|
|
317
331
|
def mime_magic_content_type
|
318
|
-
|
332
|
+
if path
|
333
|
+
File.open(path) do |file|
|
334
|
+
MimeMagic.by_magic(file).try(:type) || 'invalid/invalid'
|
335
|
+
end
|
336
|
+
end
|
319
337
|
rescue Errno::ENOENT
|
320
338
|
nil
|
321
339
|
end
|
322
340
|
|
323
|
-
def
|
324
|
-
|
341
|
+
def mini_mime_content_type
|
342
|
+
return unless path
|
343
|
+
mime_type = ::MiniMime.lookup_by_filename(path)
|
344
|
+
@content_type = (mime_type && mime_type.content_type).to_s
|
325
345
|
end
|
326
346
|
|
327
347
|
def split_extension(filename)
|
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"
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
module Storage
|
5
3
|
|
@@ -25,6 +23,21 @@ module CarrierWave
|
|
25
23
|
def retrieve!(identifier)
|
26
24
|
end
|
27
25
|
|
26
|
+
def cache!(new_file)
|
27
|
+
raise NotImplementedError.new("Need to implement #cache! if you want to use #{self.class.name} as a cache storage.")
|
28
|
+
end
|
29
|
+
|
30
|
+
def retrieve_from_cache!(identifier)
|
31
|
+
raise NotImplementedError.new("Need to implement #retrieve_from_cache! if you want to use #{self.class.name} as a cache storage.")
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete_dir!(path)
|
35
|
+
raise NotImplementedError.new("Need to implement #delete_dir! if you want to use #{self.class.name} as a cache storage.")
|
36
|
+
end
|
37
|
+
|
38
|
+
def clean_cache!(seconds)
|
39
|
+
raise NotImplementedError.new("Need to implement #clean_cache! if you want to use #{self.class.name} as a cache storage.")
|
40
|
+
end
|
28
41
|
end # Abstract
|
29
42
|
end # Storage
|
30
43
|
end # CarrierWave
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
module Storage
|
5
3
|
|
@@ -9,6 +7,10 @@ module CarrierWave
|
|
9
7
|
# pretty much it.
|
10
8
|
#
|
11
9
|
class File < Abstract
|
10
|
+
def initialize(*)
|
11
|
+
super
|
12
|
+
@cache_called = nil
|
13
|
+
end
|
12
14
|
|
13
15
|
##
|
14
16
|
# Move the file to the uploader's store path.
|
@@ -51,6 +53,71 @@ module CarrierWave
|
|
51
53
|
CarrierWave::SanitizedFile.new(path)
|
52
54
|
end
|
53
55
|
|
56
|
+
##
|
57
|
+
# Stores given file to cache directory.
|
58
|
+
#
|
59
|
+
# === Parameters
|
60
|
+
#
|
61
|
+
# [new_file (File, IOString, Tempfile)] any kind of file object
|
62
|
+
#
|
63
|
+
# === Returns
|
64
|
+
#
|
65
|
+
# [CarrierWave::SanitizedFile] a sanitized file
|
66
|
+
#
|
67
|
+
def cache!(new_file)
|
68
|
+
new_file.move_to(::File.expand_path(uploader.cache_path, uploader.root), uploader.permissions, uploader.directory_permissions, true)
|
69
|
+
rescue Errno::EMLINK, Errno::ENOSPC => e
|
70
|
+
raise(e) if @cache_called
|
71
|
+
@cache_called = true
|
72
|
+
|
73
|
+
# NOTE: Remove cached files older than 10 minutes
|
74
|
+
clean_cache!(600)
|
75
|
+
|
76
|
+
cache!(new_file)
|
77
|
+
end
|
78
|
+
|
79
|
+
##
|
80
|
+
# Retrieves the file with the given cache_name from the cache.
|
81
|
+
#
|
82
|
+
# === Parameters
|
83
|
+
#
|
84
|
+
# [cache_name (String)] uniquely identifies a cache file
|
85
|
+
#
|
86
|
+
# === Raises
|
87
|
+
#
|
88
|
+
# [CarrierWave::InvalidParameter] if the cache_name is incorrectly formatted.
|
89
|
+
#
|
90
|
+
def retrieve_from_cache!(identifier)
|
91
|
+
CarrierWave::SanitizedFile.new(::File.expand_path(uploader.cache_path(identifier), uploader.root))
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Deletes a cache dir
|
96
|
+
#
|
97
|
+
def delete_dir!(path)
|
98
|
+
if path
|
99
|
+
begin
|
100
|
+
Dir.rmdir(::File.expand_path(path, uploader.root))
|
101
|
+
rescue Errno::ENOENT
|
102
|
+
# Ignore: path does not exist
|
103
|
+
rescue Errno::ENOTDIR
|
104
|
+
# Ignore: path is not a dir
|
105
|
+
rescue Errno::ENOTEMPTY, Errno::EEXIST
|
106
|
+
# Ignore: dir is not empty
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def clean_cache!(seconds)
|
112
|
+
Dir.glob(::File.expand_path(::File.join(uploader.cache_dir, '*'), CarrierWave.root)).each do |dir|
|
113
|
+
# generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
|
114
|
+
time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
|
115
|
+
time = Time.at(*time)
|
116
|
+
if time < (Time.now.utc - seconds)
|
117
|
+
FileUtils.rm_rf(dir)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
54
121
|
end # File
|
55
122
|
end # Storage
|
56
123
|
end # CarrierWave
|
@@ -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,7 +25,7 @@ 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:
|
@@ -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,57 @@ 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 formated TIMEINT-PID(-COUNTER)-RND
|
150
|
+
time = file.key.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map { |t| t.to_i }
|
151
|
+
time = Time.at(*time)
|
152
|
+
file.destroy if time < (Time.now.utc - seconds)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
97
156
|
def connection
|
98
157
|
@connection ||= begin
|
99
158
|
options = credentials = uploader.fog_credentials
|
@@ -102,6 +161,8 @@ module CarrierWave
|
|
102
161
|
end
|
103
162
|
|
104
163
|
class File
|
164
|
+
DEFAULT_S3_REGION = 'us-east-1'
|
165
|
+
|
105
166
|
include CarrierWave::Utilities::Uri
|
106
167
|
|
107
168
|
##
|
@@ -126,7 +187,7 @@ module CarrierWave
|
|
126
187
|
|
127
188
|
##
|
128
189
|
# Return a temporary authenticated url to a private file, if available
|
129
|
-
# Only supported for AWS, Rackspace and
|
190
|
+
# Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
|
130
191
|
#
|
131
192
|
# === Returns
|
132
193
|
#
|
@@ -135,19 +196,28 @@ module CarrierWave
|
|
135
196
|
# [NilClass] no authenticated url available
|
136
197
|
#
|
137
198
|
def authenticated_url(options = {})
|
138
|
-
if ['AWS', 'Google', 'Rackspace', 'OpenStack'].include?(@uploader.fog_credentials[:provider])
|
199
|
+
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(@uploader.fog_credentials[:provider])
|
139
200
|
# avoid a get by using local references
|
140
201
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
141
202
|
local_file = local_directory.files.new(:key => path)
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
203
|
+
expire_at = options[:expire_at] || ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
|
204
|
+
case @uploader.fog_credentials[:provider]
|
205
|
+
when 'AWS', 'Google'
|
206
|
+
# Older versions of fog-google do not support options as a parameter
|
207
|
+
if url_options_supported?(local_file)
|
208
|
+
local_file.url(expire_at, options)
|
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)
|
218
|
+
else
|
219
|
+
local_file.url(expire_at)
|
148
220
|
end
|
149
|
-
else
|
150
|
-
nil
|
151
221
|
end
|
152
222
|
end
|
153
223
|
|
@@ -159,7 +229,7 @@ module CarrierWave
|
|
159
229
|
# [String] value of content-type
|
160
230
|
#
|
161
231
|
def content_type
|
162
|
-
@content_type || file.content_type
|
232
|
+
@content_type || file.try(:content_type)
|
163
233
|
end
|
164
234
|
|
165
235
|
##
|
@@ -182,7 +252,9 @@ module CarrierWave
|
|
182
252
|
#
|
183
253
|
def delete
|
184
254
|
# avoid a get by just using local reference
|
185
|
-
directory.files.new(:key => path).destroy
|
255
|
+
directory.files.new(:key => path).destroy.tap do |result|
|
256
|
+
@file = nil if result
|
257
|
+
end
|
186
258
|
end
|
187
259
|
|
188
260
|
##
|
@@ -213,7 +285,7 @@ module CarrierWave
|
|
213
285
|
end
|
214
286
|
|
215
287
|
def initialize(uploader, base, path)
|
216
|
-
@uploader, @base, @path = uploader, base, path
|
288
|
+
@uploader, @base, @path, @content_type = uploader, base, path, nil
|
217
289
|
end
|
218
290
|
|
219
291
|
##
|
@@ -223,6 +295,16 @@ module CarrierWave
|
|
223
295
|
#
|
224
296
|
# [String] contents of file
|
225
297
|
def read
|
298
|
+
file_body = file.body
|
299
|
+
|
300
|
+
return if file_body.nil?
|
301
|
+
return file_body unless file_body.is_a?(::File)
|
302
|
+
|
303
|
+
# Fog::Storage::XXX::File#body could return the source file which was upoloaded to the remote server.
|
304
|
+
read_source_file(file_body) if ::File.exist?(file_body.path)
|
305
|
+
|
306
|
+
# If the source file doesn't exist, the remote content is read
|
307
|
+
@file = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
226
308
|
file.body
|
227
309
|
end
|
228
310
|
|
@@ -234,7 +316,7 @@ module CarrierWave
|
|
234
316
|
# [Integer] size of file body
|
235
317
|
#
|
236
318
|
def size
|
237
|
-
file.content_length
|
319
|
+
file.nil? ? 0 : file.content_length
|
238
320
|
end
|
239
321
|
|
240
322
|
##
|
@@ -244,7 +326,7 @@ module CarrierWave
|
|
244
326
|
#
|
245
327
|
# [Boolean] true if file exists or false
|
246
328
|
def exists?
|
247
|
-
!!
|
329
|
+
!!file
|
248
330
|
end
|
249
331
|
|
250
332
|
##
|
@@ -254,15 +336,19 @@ module CarrierWave
|
|
254
336
|
#
|
255
337
|
# [Boolean] true on success or raises error
|
256
338
|
def store(new_file)
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
339
|
+
if new_file.is_a?(self.class)
|
340
|
+
new_file.copy_to(path)
|
341
|
+
else
|
342
|
+
fog_file = new_file.to_file
|
343
|
+
@content_type ||= new_file.content_type
|
344
|
+
@file = directory.files.create({
|
345
|
+
:body => fog_file ? fog_file : new_file.read,
|
346
|
+
:content_type => @content_type,
|
347
|
+
:key => path,
|
348
|
+
:public => @uploader.fog_public
|
349
|
+
}.merge(@uploader.fog_attributes))
|
350
|
+
fog_file.close if fog_file && !fog_file.closed?
|
351
|
+
end
|
266
352
|
true
|
267
353
|
end
|
268
354
|
|
@@ -285,23 +371,35 @@ module CarrierWave
|
|
285
371
|
end
|
286
372
|
else
|
287
373
|
# AWS/Google optimized for speed over correctness
|
288
|
-
case
|
374
|
+
case fog_provider
|
289
375
|
when 'AWS'
|
290
376
|
# check if some endpoint is set in fog_credentials
|
291
377
|
if @uploader.fog_credentials.has_key?(:endpoint)
|
292
378
|
"#{@uploader.fog_credentials[:endpoint]}/#{@uploader.fog_directory}/#{encoded_path}"
|
293
379
|
else
|
294
380
|
protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
|
381
|
+
|
382
|
+
subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
|
383
|
+
valid_subdomain = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
|
384
|
+
|
295
385
|
# if directory is a valid subdomain, use that style for access
|
296
|
-
if
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
386
|
+
if valid_subdomain
|
387
|
+
s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
|
388
|
+
"#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
|
389
|
+
else # directory is not a valid subdomain, so use path style for access
|
390
|
+
region = @uploader.fog_credentials[:region].to_s
|
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}"
|
301
398
|
end
|
302
399
|
end
|
303
400
|
when 'Google'
|
304
|
-
|
401
|
+
# https://cloud.google.com/storage/docs/access-public-data
|
402
|
+
"https://storage.googleapis.com/#{@uploader.fog_directory}/#{encoded_path}"
|
305
403
|
else
|
306
404
|
# avoid a get by just using local reference
|
307
405
|
directory.files.new(:key => path).public_url
|
@@ -336,9 +434,24 @@ module CarrierWave
|
|
336
434
|
# [NilClass] no file name available
|
337
435
|
#
|
338
436
|
def filename(options = {})
|
339
|
-
|
340
|
-
|
341
|
-
|
437
|
+
return unless file_url = url(options)
|
438
|
+
CGI.unescape(file_url.split('?').first).gsub(/.*\/(.*?$)/, '\1')
|
439
|
+
end
|
440
|
+
|
441
|
+
##
|
442
|
+
# Creates a copy of this file and returns it.
|
443
|
+
#
|
444
|
+
# === Parameters
|
445
|
+
#
|
446
|
+
# [new_path (String)] The path where the file should be copied to.
|
447
|
+
#
|
448
|
+
# === Returns
|
449
|
+
#
|
450
|
+
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
451
|
+
#
|
452
|
+
def copy_to(new_path)
|
453
|
+
connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, acl_header)
|
454
|
+
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
342
455
|
end
|
343
456
|
|
344
457
|
private
|
@@ -381,6 +494,33 @@ module CarrierWave
|
|
381
494
|
@file ||= directory.files.head(path)
|
382
495
|
end
|
383
496
|
|
497
|
+
def acl_header
|
498
|
+
if fog_provider == 'AWS'
|
499
|
+
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
500
|
+
else
|
501
|
+
{}
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
def fog_provider
|
506
|
+
@uploader.fog_credentials[:provider].to_s
|
507
|
+
end
|
508
|
+
|
509
|
+
def read_source_file(file_body)
|
510
|
+
return unless ::File.exist?(file_body.path)
|
511
|
+
|
512
|
+
begin
|
513
|
+
file_body = ::File.open(file_body.path) if file_body.closed? # Reopen if it's already closed
|
514
|
+
file_body.read
|
515
|
+
ensure
|
516
|
+
file_body.close
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
def url_options_supported?(local_file)
|
521
|
+
parameters = local_file.method(:url).parameters
|
522
|
+
parameters.count == 2 && parameters[1].include?(:options)
|
523
|
+
end
|
384
524
|
end
|
385
525
|
|
386
526
|
end # Fog
|