carrierwave 1.3.1 → 2.2.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 +102 -43
- data/lib/carrierwave/downloader/base.rb +87 -0
- data/lib/carrierwave/downloader/remote_file.rb +65 -0
- data/lib/carrierwave/locale/en.yml +5 -4
- data/lib/carrierwave/mount.rb +25 -19
- data/lib/carrierwave/mounter.rb +71 -48
- data/lib/carrierwave/orm/activerecord.rb +14 -8
- data/lib/carrierwave/processing/mini_magick.rb +100 -117
- data/lib/carrierwave/processing/rmagick.rb +11 -5
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +45 -23
- data/lib/carrierwave/storage/file.rb +2 -2
- data/lib/carrierwave/storage/fog.rb +42 -14
- data/lib/carrierwave/storage.rb +1 -0
- data/lib/carrierwave/uploader/cache.rb +24 -16
- data/lib/carrierwave/uploader/configuration.rb +28 -15
- data/lib/carrierwave/uploader/content_type_blacklist.rb +17 -8
- data/lib/carrierwave/uploader/content_type_whitelist.rb +20 -8
- data/lib/carrierwave/uploader/download.rb +2 -80
- data/lib/carrierwave/uploader/extension_blacklist.rb +18 -10
- data/lib/carrierwave/uploader/extension_whitelist.rb +19 -10
- data/lib/carrierwave/uploader/mountable.rb +6 -0
- data/lib/carrierwave/uploader/proxy.rb +2 -2
- data/lib/carrierwave/uploader/serialization.rb +1 -1
- data/lib/carrierwave/uploader/store.rb +5 -3
- data/lib/carrierwave/uploader/url.rb +6 -3
- data/lib/carrierwave/uploader/versions.rb +43 -13
- data/lib/carrierwave/uploader.rb +0 -9
- data/lib/carrierwave/validations/active_model.rb +3 -3
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +4 -0
- data/lib/generators/templates/uploader.rb +2 -2
- metadata +93 -21
@@ -0,0 +1,284 @@
|
|
1
|
+
module CarrierWave
|
2
|
+
|
3
|
+
##
|
4
|
+
# This module simplifies manipulation with vips by providing a set
|
5
|
+
# of convenient helper methods. If you want to use them, you'll need to
|
6
|
+
# require this file:
|
7
|
+
#
|
8
|
+
# require 'carrierwave/processing/vips'
|
9
|
+
#
|
10
|
+
# And then include it in your uploader:
|
11
|
+
#
|
12
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
13
|
+
# include CarrierWave::Vips
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# You can now use the provided helpers:
|
17
|
+
#
|
18
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
19
|
+
# include CarrierWave::Vips
|
20
|
+
#
|
21
|
+
# process :resize_to_fit => [200, 200]
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Or create your own helpers with the powerful vips! method, which
|
25
|
+
# yields an ImageProcessing::Builder object. Check out the ImageProcessing
|
26
|
+
# docs at http://github.com/janko-m/image_processing and the list of all
|
27
|
+
# available Vips options at
|
28
|
+
# https://libvips.github.io/libvips/API/current/using-cli.html for more info.
|
29
|
+
#
|
30
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
31
|
+
# include CarrierWave::Vips
|
32
|
+
#
|
33
|
+
# process :radial_blur => 10
|
34
|
+
#
|
35
|
+
# def radial_blur(amount)
|
36
|
+
# vips! do |builder|
|
37
|
+
# builder.radial_blur(amount)
|
38
|
+
# builder = yield(builder) if block_given?
|
39
|
+
# builder
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# === Note
|
45
|
+
#
|
46
|
+
# The ImageProcessing gem uses ruby-vips, a binding for the vips image
|
47
|
+
# library. You can find more information here:
|
48
|
+
#
|
49
|
+
# https://github.com/libvips/ruby-vips
|
50
|
+
#
|
51
|
+
#
|
52
|
+
module Vips
|
53
|
+
extend ActiveSupport::Concern
|
54
|
+
|
55
|
+
included do
|
56
|
+
require "image_processing/vips"
|
57
|
+
# We need to disable caching since we're editing images in place.
|
58
|
+
::Vips.cache_set_max(0)
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
def convert(format)
|
63
|
+
process :convert => format
|
64
|
+
end
|
65
|
+
|
66
|
+
def resize_to_limit(width, height)
|
67
|
+
process :resize_to_limit => [width, height]
|
68
|
+
end
|
69
|
+
|
70
|
+
def resize_to_fit(width, height)
|
71
|
+
process :resize_to_fit => [width, height]
|
72
|
+
end
|
73
|
+
|
74
|
+
def resize_to_fill(width, height, gravity='centre')
|
75
|
+
process :resize_to_fill => [width, height, gravity]
|
76
|
+
end
|
77
|
+
|
78
|
+
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil)
|
79
|
+
process :resize_and_pad => [width, height, background, gravity, alpha]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Changes the image encoding format to the given format
|
85
|
+
#
|
86
|
+
# See https://libvips.github.io/libvips/API/current/using-cli.html#using-command-line-conversion
|
87
|
+
#
|
88
|
+
# === Parameters
|
89
|
+
#
|
90
|
+
# [format (#to_s)] an abbreviation of the format
|
91
|
+
#
|
92
|
+
# === Yields
|
93
|
+
#
|
94
|
+
# [Vips::Image] additional manipulations to perform
|
95
|
+
#
|
96
|
+
# === Examples
|
97
|
+
#
|
98
|
+
# image.convert(:png)
|
99
|
+
#
|
100
|
+
def convert(format, page=nil)
|
101
|
+
vips! do |builder|
|
102
|
+
builder = builder.convert(format)
|
103
|
+
builder = builder.loader(page: page) if page
|
104
|
+
builder
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Resize the image to fit within the specified dimensions while retaining
|
110
|
+
# the original aspect ratio. Will only resize the image if it is larger than the
|
111
|
+
# specified dimensions. The resulting image may be shorter or narrower than specified
|
112
|
+
# in the smaller dimension but will not be larger than the specified values.
|
113
|
+
#
|
114
|
+
# === Parameters
|
115
|
+
#
|
116
|
+
# [width (Integer)] the width to scale the image to
|
117
|
+
# [height (Integer)] the height to scale the image to
|
118
|
+
# [combine_options (Hash)] additional Vips options to apply before resizing
|
119
|
+
#
|
120
|
+
# === Yields
|
121
|
+
#
|
122
|
+
# [Vips::Image] additional manipulations to perform
|
123
|
+
#
|
124
|
+
def resize_to_limit(width, height, combine_options: {})
|
125
|
+
width, height = resolve_dimensions(width, height)
|
126
|
+
|
127
|
+
vips! do |builder|
|
128
|
+
builder.resize_to_limit(width, height)
|
129
|
+
.apply(combine_options)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Resize the image to fit within the specified dimensions while retaining
|
135
|
+
# the original aspect ratio. The image may be shorter or narrower than
|
136
|
+
# specified in the smaller dimension but will not be larger than the specified values.
|
137
|
+
#
|
138
|
+
# === Parameters
|
139
|
+
#
|
140
|
+
# [width (Integer)] the width to scale the image to
|
141
|
+
# [height (Integer)] the height to scale the image to
|
142
|
+
# [combine_options (Hash)] additional Vips options to apply before resizing
|
143
|
+
#
|
144
|
+
# === Yields
|
145
|
+
#
|
146
|
+
# [Vips::Image] additional manipulations to perform
|
147
|
+
#
|
148
|
+
def resize_to_fit(width, height, combine_options: {})
|
149
|
+
width, height = resolve_dimensions(width, height)
|
150
|
+
|
151
|
+
vips! do |builder|
|
152
|
+
builder.resize_to_fit(width, height)
|
153
|
+
.apply(combine_options)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Resize the image to fit within the specified dimensions while retaining
|
159
|
+
# the aspect ratio of the original image. If necessary, crop the image in the
|
160
|
+
# larger dimension.
|
161
|
+
#
|
162
|
+
# === Parameters
|
163
|
+
#
|
164
|
+
# [width (Integer)] the width to scale the image to
|
165
|
+
# [height (Integer)] the height to scale the image to
|
166
|
+
# [combine_options (Hash)] additional vips options to apply before resizing
|
167
|
+
#
|
168
|
+
# === Yields
|
169
|
+
#
|
170
|
+
# [Vips::Image] additional manipulations to perform
|
171
|
+
#
|
172
|
+
def resize_to_fill(width, height, _gravity = nil, combine_options: {})
|
173
|
+
width, height = resolve_dimensions(width, height)
|
174
|
+
|
175
|
+
vips! do |builder|
|
176
|
+
builder.resize_to_fill(width, height).apply(combine_options)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Resize the image to fit within the specified dimensions while retaining
|
182
|
+
# the original aspect ratio. If necessary, will pad the remaining area
|
183
|
+
# with the given color, which defaults to transparent (for gif and png,
|
184
|
+
# white for jpeg).
|
185
|
+
#
|
186
|
+
# See https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsCompassDirection
|
187
|
+
# for gravity options.
|
188
|
+
#
|
189
|
+
# === Parameters
|
190
|
+
#
|
191
|
+
# [width (Integer)] the width to scale the image to
|
192
|
+
# [height (Integer)] the height to scale the image to
|
193
|
+
# [background (List, nil)] the color of the background as a RGB, like [0, 255, 255], nil indicates transparent
|
194
|
+
# [gravity (String)] how to position the image
|
195
|
+
# [alpha (Boolean, nil)] pad the image with the alpha channel if supported
|
196
|
+
# [combine_options (Hash)] additional vips options to apply before resizing
|
197
|
+
#
|
198
|
+
# === Yields
|
199
|
+
#
|
200
|
+
# [Vips::Image] additional manipulations to perform
|
201
|
+
#
|
202
|
+
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil, combine_options: {})
|
203
|
+
width, height = resolve_dimensions(width, height)
|
204
|
+
|
205
|
+
vips! do |builder|
|
206
|
+
builder.resize_and_pad(width, height, background: background, gravity: gravity, alpha: alpha)
|
207
|
+
.apply(combine_options)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Returns the width of the image in pixels.
|
213
|
+
#
|
214
|
+
# === Returns
|
215
|
+
#
|
216
|
+
# [Integer] the image's width in pixels
|
217
|
+
#
|
218
|
+
def width
|
219
|
+
vips_image.width
|
220
|
+
end
|
221
|
+
|
222
|
+
##
|
223
|
+
# Returns the height of the image in pixels.
|
224
|
+
#
|
225
|
+
# === Returns
|
226
|
+
#
|
227
|
+
# [Integer] the image's height in pixels
|
228
|
+
#
|
229
|
+
def height
|
230
|
+
vips_image.height
|
231
|
+
end
|
232
|
+
|
233
|
+
# Process the image with vip, using the ImageProcessing gem. This
|
234
|
+
# method will build a "convert" vips command and execute it on the
|
235
|
+
# current image.
|
236
|
+
#
|
237
|
+
# === Gotcha
|
238
|
+
#
|
239
|
+
# This method assumes that the object responds to +current_path+.
|
240
|
+
# Any class that this module is mixed into must have a +current_path+ method.
|
241
|
+
# CarrierWave::Uploader does, so you won't need to worry about this in
|
242
|
+
# most cases.
|
243
|
+
#
|
244
|
+
# === Yields
|
245
|
+
#
|
246
|
+
# [ImageProcessing::Builder] use it to define processing to be performed
|
247
|
+
#
|
248
|
+
# === Raises
|
249
|
+
#
|
250
|
+
# [CarrierWave::ProcessingError] if processing failed.
|
251
|
+
def vips!
|
252
|
+
builder = ImageProcessing::Vips.source(current_path)
|
253
|
+
builder = yield(builder)
|
254
|
+
|
255
|
+
result = builder.call
|
256
|
+
result.close
|
257
|
+
|
258
|
+
FileUtils.mv result.path, current_path
|
259
|
+
|
260
|
+
if File.extname(result.path) != File.extname(current_path)
|
261
|
+
move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
|
262
|
+
file.content_type = ::MiniMime.lookup_by_filename(move_to).content_type
|
263
|
+
file.move_to(move_to, permissions, directory_permissions)
|
264
|
+
end
|
265
|
+
rescue ::Vips::Error => e
|
266
|
+
message = I18n.translate(:"errors.messages.vips_processing_error", :e => e)
|
267
|
+
raise CarrierWave::ProcessingError, message
|
268
|
+
end
|
269
|
+
|
270
|
+
private
|
271
|
+
|
272
|
+
def resolve_dimensions(*dimensions)
|
273
|
+
dimensions.map do |value|
|
274
|
+
next value unless value.instance_of?(Proc)
|
275
|
+
value.arity >= 1 ? value.call(self) : value.call
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def vips_image
|
280
|
+
::Vips::Image.new_from_buffer(read, "")
|
281
|
+
end
|
282
|
+
|
283
|
+
end # Vips
|
284
|
+
end # CarrierWave
|
@@ -1,12 +1,8 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'active_support/core_ext/string/multibyte'
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
require 'mime/types/columnar'
|
7
|
-
rescue LoadError
|
8
|
-
require 'mime/types'
|
9
|
-
end
|
3
|
+
require 'mini_mime'
|
4
|
+
require 'mimemagic'
|
5
|
+
require 'mimemagic/overlay'
|
10
6
|
|
11
7
|
module CarrierWave
|
12
8
|
|
@@ -114,12 +110,11 @@ module CarrierWave
|
|
114
110
|
# [String, nil] the path where the file is located.
|
115
111
|
#
|
116
112
|
def path
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
113
|
+
return if @file.blank?
|
114
|
+
if is_path?
|
115
|
+
File.expand_path(@file)
|
116
|
+
elsif @file.respond_to?(:path) && !@file.path.blank?
|
117
|
+
File.expand_path(@file.path)
|
123
118
|
end
|
124
119
|
end
|
125
120
|
|
@@ -187,9 +182,9 @@ module CarrierWave
|
|
187
182
|
move!(new_path)
|
188
183
|
chmod!(new_path, permissions)
|
189
184
|
if keep_filename
|
190
|
-
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
|
185
|
+
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
|
191
186
|
else
|
192
|
-
self.file = {:tempfile => new_path, :content_type => content_type}
|
187
|
+
self.file = {:tempfile => new_path, :content_type => @content_type}
|
193
188
|
end
|
194
189
|
self
|
195
190
|
end
|
@@ -265,12 +260,10 @@ module CarrierWave
|
|
265
260
|
# [String] the content type of the file
|
266
261
|
#
|
267
262
|
def content_type
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
@content_type = ::MIME::Types.type_for(path).first.to_s
|
273
|
-
end
|
263
|
+
@content_type ||=
|
264
|
+
existing_content_type ||
|
265
|
+
mime_magic_content_type ||
|
266
|
+
mini_mime_content_type
|
274
267
|
end
|
275
268
|
|
276
269
|
##
|
@@ -313,7 +306,7 @@ module CarrierWave
|
|
313
306
|
def mkdir!(path, directory_permissions)
|
314
307
|
options = {}
|
315
308
|
options[:mode] = directory_permissions if directory_permissions
|
316
|
-
FileUtils.mkdir_p(File.dirname(path), options) unless File.exist?(File.dirname(path))
|
309
|
+
FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
|
317
310
|
end
|
318
311
|
|
319
312
|
def chmod!(path, permissions)
|
@@ -326,10 +319,39 @@ module CarrierWave
|
|
326
319
|
name = File.basename(name)
|
327
320
|
name = name.gsub(sanitize_regexp,"_")
|
328
321
|
name = "_#{name}" if name =~ /\A\.+\z/
|
329
|
-
name = "unnamed" if name.size
|
322
|
+
name = "unnamed" if name.size.zero?
|
330
323
|
return name.mb_chars.to_s
|
331
324
|
end
|
332
325
|
|
326
|
+
def existing_content_type
|
327
|
+
if @file.respond_to?(:content_type) && @file.content_type
|
328
|
+
@file.content_type.to_s.chomp
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def mime_magic_content_type
|
333
|
+
if path
|
334
|
+
type = File.open(path) do |file|
|
335
|
+
MimeMagic.by_magic(file).try(:type)
|
336
|
+
end
|
337
|
+
|
338
|
+
if type.nil?
|
339
|
+
type = ::MiniMime.lookup_by_filename(path).try(:content_type)
|
340
|
+
type = 'invalid/invalid' unless type.nil? || type.start_with?('text/')
|
341
|
+
end
|
342
|
+
|
343
|
+
type
|
344
|
+
end
|
345
|
+
rescue Errno::ENOENT
|
346
|
+
nil
|
347
|
+
end
|
348
|
+
|
349
|
+
def mini_mime_content_type
|
350
|
+
return unless path
|
351
|
+
mime_type = ::MiniMime.lookup_by_filename(path)
|
352
|
+
@content_type = (mime_type && mime_type.content_type).to_s
|
353
|
+
end
|
354
|
+
|
333
355
|
def split_extension(filename)
|
334
356
|
# regular expressions to try for identifying extensions
|
335
357
|
extension_matchers = [
|
@@ -110,8 +110,8 @@ module CarrierWave
|
|
110
110
|
|
111
111
|
def clean_cache!(seconds)
|
112
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
|
113
|
+
# generate_cache_id returns key formated TIMEINT-PID(-COUNTER)-RND
|
114
|
+
time = dir.scan(/(\d+)-\d+-\d+(?:-\d+)?/).first.map(&:to_i)
|
115
115
|
time = Time.at(*time)
|
116
116
|
if time < (Time.now.utc - seconds)
|
117
117
|
FileUtils.rm_rf(dir)
|
@@ -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,8 +146,8 @@ 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 formated TIMEINT-PID-COUNTER-RND
|
142
|
-
time = file.key.scan(/(\d+)-\d+-\d
|
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 }
|
143
151
|
time = Time.at(*time)
|
144
152
|
file.destroy if time < (Time.now.utc - seconds)
|
145
153
|
end
|
@@ -153,6 +161,8 @@ module CarrierWave
|
|
153
161
|
end
|
154
162
|
|
155
163
|
class File
|
164
|
+
DEFAULT_S3_REGION = 'us-east-1'
|
165
|
+
|
156
166
|
include CarrierWave::Utilities::Uri
|
157
167
|
|
158
168
|
##
|
@@ -177,7 +187,7 @@ module CarrierWave
|
|
177
187
|
|
178
188
|
##
|
179
189
|
# Return a temporary authenticated url to a private file, if available
|
180
|
-
# Only supported for AWS, Rackspace and
|
190
|
+
# Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
|
181
191
|
#
|
182
192
|
# === Returns
|
183
193
|
#
|
@@ -186,11 +196,11 @@ module CarrierWave
|
|
186
196
|
# [NilClass] no authenticated url available
|
187
197
|
#
|
188
198
|
def authenticated_url(options = {})
|
189
|
-
if ['AWS', 'Google', 'Rackspace', 'OpenStack'].include?(@uploader.fog_credentials[:provider])
|
199
|
+
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(@uploader.fog_credentials[:provider])
|
190
200
|
# avoid a get by using local references
|
191
201
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
192
202
|
local_file = local_directory.files.new(:key => path)
|
193
|
-
expire_at = ::Fog::Time.now
|
203
|
+
expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
|
194
204
|
case @uploader.fog_credentials[:provider]
|
195
205
|
when 'AWS', 'Google'
|
196
206
|
# Older versions of fog-google do not support options as a parameter
|
@@ -200,10 +210,11 @@ module CarrierWave
|
|
200
210
|
warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
|
201
211
|
local_file.url(expire_at)
|
202
212
|
end
|
203
|
-
when 'Rackspace'
|
213
|
+
when 'Rackspace', 'OpenStack'
|
204
214
|
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
|
205
|
-
when '
|
206
|
-
|
215
|
+
when 'Aliyun'
|
216
|
+
expire_at = expire_at - Time.now
|
217
|
+
local_file.url(expire_at)
|
207
218
|
else
|
208
219
|
local_file.url(expire_at)
|
209
220
|
end
|
@@ -218,7 +229,7 @@ module CarrierWave
|
|
218
229
|
# [String] value of content-type
|
219
230
|
#
|
220
231
|
def content_type
|
221
|
-
@content_type ||
|
232
|
+
@content_type || file.try(:content_type)
|
222
233
|
end
|
223
234
|
|
224
235
|
##
|
@@ -241,7 +252,9 @@ module CarrierWave
|
|
241
252
|
#
|
242
253
|
def delete
|
243
254
|
# avoid a get by just using local reference
|
244
|
-
directory.files.new(:key => path).destroy
|
255
|
+
directory.files.new(:key => path).destroy.tap do |result|
|
256
|
+
@file = nil if result
|
257
|
+
end
|
245
258
|
end
|
246
259
|
|
247
260
|
##
|
@@ -373,9 +386,15 @@ module CarrierWave
|
|
373
386
|
if valid_subdomain
|
374
387
|
s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
|
375
388
|
"#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
|
376
|
-
else
|
377
|
-
|
378
|
-
|
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}"
|
379
398
|
end
|
380
399
|
end
|
381
400
|
when 'Google'
|
@@ -431,7 +450,7 @@ module CarrierWave
|
|
431
450
|
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
432
451
|
#
|
433
452
|
def copy_to(new_path)
|
434
|
-
connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path,
|
453
|
+
connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, copy_options)
|
435
454
|
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
436
455
|
end
|
437
456
|
|
@@ -475,9 +494,18 @@ module CarrierWave
|
|
475
494
|
@file ||= directory.files.head(path)
|
476
495
|
end
|
477
496
|
|
497
|
+
def copy_options
|
498
|
+
options = {}
|
499
|
+
options.merge!(acl_header) if acl_header.present?
|
500
|
+
options['Content-Type'] ||= content_type if content_type
|
501
|
+
options.merge(@uploader.fog_attributes)
|
502
|
+
end
|
503
|
+
|
478
504
|
def acl_header
|
479
505
|
if fog_provider == 'AWS'
|
480
506
|
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
507
|
+
elsif fog_provider == "Google"
|
508
|
+
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
481
509
|
else
|
482
510
|
{}
|
483
511
|
end
|
data/lib/carrierwave/storage.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
1
3
|
module CarrierWave
|
2
4
|
|
3
5
|
class FormNotMultipart < UploadError
|
@@ -23,9 +25,9 @@ module CarrierWave
|
|
23
25
|
#
|
24
26
|
def self.generate_cache_id
|
25
27
|
[Time.now.utc.to_i,
|
26
|
-
|
27
|
-
'%04d' % (CarrierWave::CacheCounter.increment %
|
28
|
-
'%04d' %
|
28
|
+
SecureRandom.random_number(1_000_000_000_000_000),
|
29
|
+
'%04d' % (CarrierWave::CacheCounter.increment % 10_000),
|
30
|
+
'%04d' % SecureRandom.random_number(10_000)
|
29
31
|
].map(&:to_s).join('-')
|
30
32
|
end
|
31
33
|
|
@@ -36,6 +38,16 @@ module CarrierWave
|
|
36
38
|
include CarrierWave::Uploader::Callbacks
|
37
39
|
include CarrierWave::Uploader::Configuration
|
38
40
|
|
41
|
+
included do
|
42
|
+
prepend Module.new {
|
43
|
+
def initialize(*)
|
44
|
+
super
|
45
|
+
@staged = false
|
46
|
+
end
|
47
|
+
}
|
48
|
+
attr_accessor :staged
|
49
|
+
end
|
50
|
+
|
39
51
|
module ClassMethods
|
40
52
|
|
41
53
|
##
|
@@ -52,7 +64,7 @@ module CarrierWave
|
|
52
64
|
# It's recommended that you keep cache files in one place only.
|
53
65
|
#
|
54
66
|
def clean_cached_files!(seconds=60*60*24)
|
55
|
-
cache_storage.new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
|
67
|
+
(cache_storage || storage).new(CarrierWave::Uploader::Base.new).clean_cache!(seconds)
|
56
68
|
end
|
57
69
|
end
|
58
70
|
|
@@ -64,7 +76,7 @@ module CarrierWave
|
|
64
76
|
# [Bool] whether the current file is cached
|
65
77
|
#
|
66
78
|
def cached?
|
67
|
-
|
79
|
+
!!@cache_id
|
68
80
|
end
|
69
81
|
|
70
82
|
##
|
@@ -78,14 +90,8 @@ module CarrierWave
|
|
78
90
|
end
|
79
91
|
|
80
92
|
def sanitized_file
|
81
|
-
|
82
|
-
|
83
|
-
sanitized = CarrierWave::Storage::Fog.new(self).retrieve!(File.basename(_content.path))
|
84
|
-
else
|
85
|
-
sanitized = SanitizedFile.new :tempfile => StringIO.new(_content),
|
86
|
-
:filename => File.basename(path), :content_type => file.content_type
|
87
|
-
end
|
88
|
-
sanitized
|
93
|
+
ActiveSupport::Deprecation.warn('#sanitized_file is deprecated, use #file instead.')
|
94
|
+
file
|
89
95
|
end
|
90
96
|
|
91
97
|
##
|
@@ -96,7 +102,7 @@ module CarrierWave
|
|
96
102
|
# [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
|
97
103
|
#
|
98
104
|
def cache_name
|
99
|
-
File.join(cache_id, full_original_filename) if cache_id
|
105
|
+
File.join(cache_id, full_original_filename) if cache_id && original_filename
|
100
106
|
end
|
101
107
|
|
102
108
|
##
|
@@ -115,7 +121,7 @@ module CarrierWave
|
|
115
121
|
#
|
116
122
|
# [CarrierWave::FormNotMultipart] if the assigned parameter is a string
|
117
123
|
#
|
118
|
-
def cache!(new_file =
|
124
|
+
def cache!(new_file = file)
|
119
125
|
new_file = CarrierWave::SanitizedFile.new(new_file)
|
120
126
|
return if new_file.empty?
|
121
127
|
|
@@ -123,6 +129,7 @@ module CarrierWave
|
|
123
129
|
|
124
130
|
self.cache_id = CarrierWave.generate_cache_id unless cache_id
|
125
131
|
|
132
|
+
@staged = true
|
126
133
|
@filename = new_file.filename
|
127
134
|
self.original_filename = new_file.filename
|
128
135
|
|
@@ -156,6 +163,7 @@ module CarrierWave
|
|
156
163
|
def retrieve_from_cache!(cache_name)
|
157
164
|
with_callbacks(:retrieve_from_cache, cache_name) do
|
158
165
|
self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
|
166
|
+
@staged = true
|
159
167
|
@filename = original_filename
|
160
168
|
@file = cache_storage.retrieve_from_cache!(full_filename(original_filename))
|
161
169
|
end
|
@@ -200,7 +208,7 @@ module CarrierWave
|
|
200
208
|
end
|
201
209
|
|
202
210
|
def cache_storage
|
203
|
-
@cache_storage ||= self.class.cache_storage.new(self)
|
211
|
+
@cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
|
204
212
|
end
|
205
213
|
end # Cache
|
206
214
|
end # Uploader
|