carrierwave 1.3.2 → 2.2.5
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 +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 +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 +2 -2
- 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 +96 -17
@@ -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
|
+
@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
|