carrierwave 1.3.4 → 2.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +117 -43
- data/lib/carrierwave/downloader/base.rb +93 -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 +70 -47
- data/lib/carrierwave/orm/activerecord.rb +14 -8
- data/lib/carrierwave/processing/mini_magick.rb +100 -117
- data/lib/carrierwave/processing/rmagick.rb +1 -1
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +38 -16
- data/lib/carrierwave/storage/file.rb +2 -2
- data/lib/carrierwave/storage/fog.rb +44 -13
- 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 -123
- 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/processing.rb +11 -1
- 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 +100 -27
@@ -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,7 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'active_support/core_ext/string/multibyte'
|
3
|
-
|
4
|
-
|
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 'mini_mime'
|
4
|
+
require 'marcel'
|
10
5
|
|
11
6
|
module CarrierWave
|
12
7
|
|
@@ -186,9 +181,9 @@ module CarrierWave
|
|
186
181
|
move!(new_path)
|
187
182
|
chmod!(new_path, permissions)
|
188
183
|
if keep_filename
|
189
|
-
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
|
184
|
+
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
|
190
185
|
else
|
191
|
-
self.file = {:tempfile => new_path, :content_type => content_type}
|
186
|
+
self.file = {:tempfile => new_path, :content_type => @content_type}
|
192
187
|
end
|
193
188
|
self
|
194
189
|
end
|
@@ -264,12 +259,10 @@ module CarrierWave
|
|
264
259
|
# [String] the content type of the file
|
265
260
|
#
|
266
261
|
def content_type
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
@content_type = ::MIME::Types.type_for(path).first.to_s
|
272
|
-
end
|
262
|
+
@content_type ||=
|
263
|
+
existing_content_type ||
|
264
|
+
marcel_magic_content_type ||
|
265
|
+
mini_mime_content_type
|
273
266
|
end
|
274
267
|
|
275
268
|
##
|
@@ -325,10 +318,39 @@ module CarrierWave
|
|
325
318
|
name = File.basename(name)
|
326
319
|
name = name.gsub(sanitize_regexp,"_")
|
327
320
|
name = "_#{name}" if name =~ /\A\.+\z/
|
328
|
-
name = "unnamed" if name.size
|
321
|
+
name = "unnamed" if name.size.zero?
|
329
322
|
return name.mb_chars.to_s
|
330
323
|
end
|
331
324
|
|
325
|
+
def existing_content_type
|
326
|
+
if @file.respond_to?(:content_type) && @file.content_type
|
327
|
+
Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp)
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def marcel_magic_content_type
|
332
|
+
if path
|
333
|
+
type = File.open(path) do |file|
|
334
|
+
Marcel::Magic.by_magic(file).try(:type)
|
335
|
+
end
|
336
|
+
|
337
|
+
if type.nil?
|
338
|
+
type = Marcel::Magic.by_path(path).try(:type)
|
339
|
+
type = 'invalid/invalid' unless type.nil? || type.start_with?('text/')
|
340
|
+
end
|
341
|
+
|
342
|
+
type
|
343
|
+
end
|
344
|
+
rescue Errno::ENOENT
|
345
|
+
nil
|
346
|
+
end
|
347
|
+
|
348
|
+
def mini_mime_content_type
|
349
|
+
return unless path
|
350
|
+
mime_type = ::MiniMime.lookup_by_filename(path)
|
351
|
+
@content_type = (mime_type && mime_type.content_type).to_s
|
352
|
+
end
|
353
|
+
|
332
354
|
def split_extension(filename)
|
333
355
|
# regular expressions to try for identifying extensions
|
334
356
|
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)
|
@@ -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 formated TIMEINT-PID-COUNTER-RND
|
142
|
-
|
143
|
-
|
149
|
+
# generate_cache_id returns key formated 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,6 +162,8 @@ module CarrierWave
|
|
153
162
|
end
|
154
163
|
|
155
164
|
class File
|
165
|
+
DEFAULT_S3_REGION = 'us-east-1'
|
166
|
+
|
156
167
|
include CarrierWave::Utilities::Uri
|
157
168
|
|
158
169
|
##
|
@@ -177,7 +188,7 @@ module CarrierWave
|
|
177
188
|
|
178
189
|
##
|
179
190
|
# Return a temporary authenticated url to a private file, if available
|
180
|
-
# Only supported for AWS, Rackspace, Google and
|
191
|
+
# Only supported for AWS, Rackspace, Google, AzureRM and Aliyun providers
|
181
192
|
#
|
182
193
|
# === Returns
|
183
194
|
#
|
@@ -186,11 +197,11 @@ module CarrierWave
|
|
186
197
|
# [NilClass] no authenticated url available
|
187
198
|
#
|
188
199
|
def authenticated_url(options = {})
|
189
|
-
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM'].include?(@uploader.fog_credentials[:provider])
|
200
|
+
if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM', 'Aliyun', 'backblaze'].include?(@uploader.fog_credentials[:provider])
|
190
201
|
# avoid a get by using local references
|
191
202
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
192
203
|
local_file = local_directory.files.new(:key => path)
|
193
|
-
expire_at = ::Fog::Time.now
|
204
|
+
expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
|
194
205
|
case @uploader.fog_credentials[:provider]
|
195
206
|
when 'AWS', 'Google'
|
196
207
|
# Older versions of fog-google do not support options as a parameter
|
@@ -202,6 +213,9 @@ module CarrierWave
|
|
202
213
|
end
|
203
214
|
when 'Rackspace', 'OpenStack'
|
204
215
|
connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
|
216
|
+
when 'Aliyun'
|
217
|
+
expire_at = expire_at - Time.now
|
218
|
+
local_file.url(expire_at)
|
205
219
|
else
|
206
220
|
local_file.url(expire_at)
|
207
221
|
end
|
@@ -216,7 +230,7 @@ module CarrierWave
|
|
216
230
|
# [String] value of content-type
|
217
231
|
#
|
218
232
|
def content_type
|
219
|
-
@content_type ||
|
233
|
+
@content_type || file.try(:content_type)
|
220
234
|
end
|
221
235
|
|
222
236
|
##
|
@@ -239,7 +253,9 @@ module CarrierWave
|
|
239
253
|
#
|
240
254
|
def delete
|
241
255
|
# avoid a get by just using local reference
|
242
|
-
directory.files.new(:key => path).destroy
|
256
|
+
directory.files.new(:key => path).destroy.tap do |result|
|
257
|
+
@file = nil if result
|
258
|
+
end
|
243
259
|
end
|
244
260
|
|
245
261
|
##
|
@@ -371,9 +387,15 @@ module CarrierWave
|
|
371
387
|
if valid_subdomain
|
372
388
|
s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
|
373
389
|
"#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
|
374
|
-
else
|
375
|
-
|
376
|
-
|
390
|
+
else # directory is not a valid subdomain, so use path style for access
|
391
|
+
region = @uploader.fog_credentials[:region].to_s
|
392
|
+
host = case region
|
393
|
+
when DEFAULT_S3_REGION, ''
|
394
|
+
's3.amazonaws.com'
|
395
|
+
else
|
396
|
+
"s3.#{region}.amazonaws.com"
|
397
|
+
end
|
398
|
+
"#{protocol}://#{host}/#{@uploader.fog_directory}/#{encoded_path}"
|
377
399
|
end
|
378
400
|
end
|
379
401
|
when 'Google'
|
@@ -429,7 +451,7 @@ module CarrierWave
|
|
429
451
|
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
430
452
|
#
|
431
453
|
def copy_to(new_path)
|
432
|
-
connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path,
|
454
|
+
connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, copy_options)
|
433
455
|
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
434
456
|
end
|
435
457
|
|
@@ -473,9 +495,18 @@ module CarrierWave
|
|
473
495
|
@file ||= directory.files.head(path)
|
474
496
|
end
|
475
497
|
|
498
|
+
def copy_options
|
499
|
+
options = {}
|
500
|
+
options.merge!(acl_header) if acl_header.present?
|
501
|
+
options['Content-Type'] ||= content_type if content_type
|
502
|
+
options.merge(@uploader.fog_attributes)
|
503
|
+
end
|
504
|
+
|
476
505
|
def acl_header
|
477
506
|
if fog_provider == 'AWS'
|
478
507
|
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
508
|
+
elsif fog_provider == "Google"
|
509
|
+
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
479
510
|
else
|
480
511
|
{}
|
481
512
|
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
|