shrine 0.9.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +70 -59
- data/doc/carrierwave.md +436 -0
- data/doc/direct_s3.md +20 -1
- data/doc/paperclip.md +308 -0
- data/doc/regenerating_versions.md +76 -11
- data/lib/shrine.rb +42 -46
- data/lib/shrine/plugins/activerecord.rb +12 -6
- data/lib/shrine/plugins/background_helpers.rb +5 -5
- data/lib/shrine/plugins/default_storage.rb +1 -1
- data/lib/shrine/plugins/default_url_options.rb +31 -0
- data/lib/shrine/plugins/determine_mime_type.rb +13 -3
- data/lib/shrine/plugins/direct_upload.rb +55 -106
- data/lib/shrine/plugins/hooks.rb +33 -11
- data/lib/shrine/plugins/keep_files.rb +4 -19
- data/lib/shrine/plugins/logging.rb +16 -11
- data/lib/shrine/plugins/migration_helpers.rb +4 -4
- data/lib/shrine/plugins/module_include.rb +46 -0
- data/lib/shrine/plugins/moving.rb +0 -11
- data/lib/shrine/plugins/multi_delete.rb +3 -12
- data/lib/shrine/plugins/parsed_json.rb +22 -0
- data/lib/shrine/plugins/rack_file.rb +63 -0
- data/lib/shrine/plugins/recache.rb +1 -1
- data/lib/shrine/plugins/restore_cached.rb +1 -2
- data/lib/shrine/plugins/sequel.rb +9 -3
- data/lib/shrine/plugins/validation_helpers.rb +1 -1
- data/lib/shrine/plugins/versions.rb +30 -17
- data/lib/shrine/storage/file_system.rb +62 -17
- data/lib/shrine/storage/linter.rb +8 -3
- data/lib/shrine/storage/s3.rb +84 -20
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +11 -8
- metadata +31 -40
- data/lib/shrine/plugins/delete_invalid.rb +0 -25
data/lib/shrine.rb
CHANGED
@@ -157,7 +157,7 @@ class Shrine
|
|
157
157
|
# model class. Example:
|
158
158
|
#
|
159
159
|
# class User
|
160
|
-
# include Shrine[:avatar] # alias for `Shrine.attachment
|
160
|
+
# include Shrine[:avatar] # alias for `Shrine.attachment(:avatar)`
|
161
161
|
# end
|
162
162
|
def attachment(name)
|
163
163
|
self::Attachment.new(name)
|
@@ -184,30 +184,6 @@ class Shrine
|
|
184
184
|
raise Error, "cannot convert #{object.inspect} to a #{self}::UploadedFile"
|
185
185
|
end
|
186
186
|
end
|
187
|
-
|
188
|
-
# Delete a Shrine::UploadedFile.
|
189
|
-
#
|
190
|
-
# Shrine.delete(uploaded_file)
|
191
|
-
def delete(uploaded_file, context = {})
|
192
|
-
uploader_for(uploaded_file).delete(uploaded_file, context)
|
193
|
-
end
|
194
|
-
|
195
|
-
# Checks if the object is a valid IO by checking that it responds to
|
196
|
-
# `#read`, `#eof?`, `#rewind`, `#size` and `#close`, otherwise raises
|
197
|
-
# Shrine::InvalidFile.
|
198
|
-
def io!(io)
|
199
|
-
missing_methods = IO_METHODS.reject do |m, a|
|
200
|
-
io.respond_to?(m) && [a.count, -1].include?(io.method(m).arity)
|
201
|
-
end
|
202
|
-
|
203
|
-
raise InvalidFile.new(io, missing_methods) if missing_methods.any?
|
204
|
-
end
|
205
|
-
|
206
|
-
# Instantiates the Shrine uploader instance for this file.
|
207
|
-
def uploader_for(uploaded_file)
|
208
|
-
uploaders = storages.keys.map { |key| new(key) }
|
209
|
-
uploaders.find { |uploader| uploader.uploaded?(uploaded_file) }
|
210
|
-
end
|
211
187
|
end
|
212
188
|
|
213
189
|
module InstanceMethods
|
@@ -272,7 +248,7 @@ class Shrine
|
|
272
248
|
uploaded_file.storage_key == storage_key.to_s
|
273
249
|
end
|
274
250
|
|
275
|
-
#
|
251
|
+
# Deletes the given uploaded file.
|
276
252
|
def delete(uploaded_file, context = {})
|
277
253
|
_delete(uploaded_file, context)
|
278
254
|
uploaded_file
|
@@ -345,7 +321,7 @@ class Shrine
|
|
345
321
|
)
|
346
322
|
end
|
347
323
|
|
348
|
-
# Removes the file. Called by
|
324
|
+
# Removes the file. Called by #delete.
|
349
325
|
def _delete(uploaded_file, context)
|
350
326
|
remove(uploaded_file, context)
|
351
327
|
end
|
@@ -376,9 +352,15 @@ class Shrine
|
|
376
352
|
process(io, context)
|
377
353
|
end
|
378
354
|
|
379
|
-
#
|
355
|
+
# Checks if the object is a valid IO by checking that it responds to
|
356
|
+
# `#read`, `#eof?`, `#rewind`, `#size` and `#close`, otherwise raises
|
357
|
+
# Shrine::InvalidFile.
|
380
358
|
def _enforce_io(io)
|
381
|
-
|
359
|
+
missing_methods = IO_METHODS.reject do |m, a|
|
360
|
+
io.respond_to?(m) && [a.count, -1].include?(io.method(m).arity)
|
361
|
+
end
|
362
|
+
|
363
|
+
raise InvalidFile.new(io, missing_methods) if missing_methods.any?
|
382
364
|
end
|
383
365
|
|
384
366
|
# Generates a UID to use in location for uploaded files.
|
@@ -459,7 +441,7 @@ class Shrine
|
|
459
441
|
# validation. Example:
|
460
442
|
#
|
461
443
|
# Shrine::Attacher.validate do
|
462
|
-
# if get.size > 5
|
444
|
+
# if get.size > 5*1024*1024
|
463
445
|
# errors << "is too big (max is 5 MB)"
|
464
446
|
# end
|
465
447
|
# end
|
@@ -484,7 +466,7 @@ class Shrine
|
|
484
466
|
# file (e.g. when it persisted after validation errors).
|
485
467
|
# Otherwise it assumes that it's an IO object and caches it.
|
486
468
|
def assign(value)
|
487
|
-
if value.is_a?(String)
|
469
|
+
if value.is_a?(String)
|
488
470
|
assign_cached(value) unless value == ""
|
489
471
|
else
|
490
472
|
uploaded_file = cache!(value, phase: :cache) if value
|
@@ -495,7 +477,7 @@ class Shrine
|
|
495
477
|
# Assigns a Shrine::UploadedFile, runs validation and schedules the
|
496
478
|
# old file for deletion.
|
497
479
|
def set(uploaded_file)
|
498
|
-
@
|
480
|
+
@old = get unless get == uploaded_file
|
499
481
|
_set(uploaded_file)
|
500
482
|
validate
|
501
483
|
|
@@ -507,20 +489,30 @@ class Shrine
|
|
507
489
|
uploaded_file(read) if read
|
508
490
|
end
|
509
491
|
|
492
|
+
# Returns true if a new file has been attached.
|
493
|
+
def attached?
|
494
|
+
instance_variable_defined?("@old")
|
495
|
+
end
|
496
|
+
|
510
497
|
# Plugins can override this if they want something to be done on save.
|
511
498
|
def save
|
512
499
|
end
|
513
500
|
|
501
|
+
# Deletes the old file and promotes the new one. Typically this should
|
502
|
+
# be called after saving.
|
503
|
+
def finalize
|
504
|
+
replace
|
505
|
+
_promote
|
506
|
+
end
|
507
|
+
|
514
508
|
# Calls #promote if attached file is cached.
|
515
509
|
def _promote
|
516
510
|
promote(get) if promote?(get)
|
517
511
|
end
|
518
512
|
|
519
|
-
# Promotes a cached file to store,
|
520
|
-
#
|
521
|
-
#
|
522
|
-
# jobs in case the user quickly changes their mind and replaces the
|
523
|
-
# attachment before the old one was finished promoting.
|
513
|
+
# Promotes a cached file to store, taking into account to check whether
|
514
|
+
# the attachment has changed in the meanwhile. Afterwards the cached
|
515
|
+
# file is deleted.
|
524
516
|
def promote(cached_file)
|
525
517
|
stored_file = store!(cached_file, phase: :store)
|
526
518
|
unless changed?(cached_file)
|
@@ -528,21 +520,20 @@ class Shrine
|
|
528
520
|
else
|
529
521
|
delete!(stored_file, phase: :stored)
|
530
522
|
end
|
531
|
-
delete!(cached_file, phase: :cached)
|
532
523
|
end
|
533
524
|
|
534
|
-
# Deletes the attachment that was replaced
|
535
|
-
#
|
536
|
-
#
|
525
|
+
# Deletes the attachment that was replaced, and is called after saving
|
526
|
+
# by ORM integrations. If also removes `@old` so that #save and #finalize
|
527
|
+
# don't get called for the current attachment anymore.
|
537
528
|
def replace
|
538
|
-
delete!(@
|
539
|
-
@
|
529
|
+
delete!(@old, phase: :replaced) if @old && !cache.uploaded?(@old)
|
530
|
+
remove_instance_variable("@old")
|
540
531
|
end
|
541
532
|
|
542
533
|
# Deletes the attachment. Typically this should be called after
|
543
534
|
# destroying a record.
|
544
535
|
def destroy
|
545
|
-
delete!(get, phase: :destroyed) if get
|
536
|
+
delete!(get, phase: :destroyed) if get && !cache.uploaded?(get)
|
546
537
|
end
|
547
538
|
|
548
539
|
# Returns the URL to the attached file (internally calls `#url` on the
|
@@ -600,9 +591,9 @@ class Shrine
|
|
600
591
|
store.upload(io, context.merge(phase: phase))
|
601
592
|
end
|
602
593
|
|
603
|
-
# Deletes the file (calls `Shrine
|
594
|
+
# Deletes the file (calls `Shrine#delete`).
|
604
595
|
def delete!(uploaded_file, phase:)
|
605
|
-
|
596
|
+
store.delete(uploaded_file, context.merge(phase: phase))
|
606
597
|
end
|
607
598
|
|
608
599
|
# Delegates to `Shrine#default_url`.
|
@@ -792,6 +783,11 @@ class Shrine
|
|
792
783
|
self.class.shrine_class
|
793
784
|
end
|
794
785
|
|
786
|
+
# Show only the data hash in inspect output.
|
787
|
+
def inspect
|
788
|
+
"#{to_s.chomp(">")} @data=#{data.inspect}>"
|
789
|
+
end
|
790
|
+
|
795
791
|
private
|
796
792
|
|
797
793
|
def io
|
@@ -14,9 +14,16 @@ class Shrine
|
|
14
14
|
# * `after_commit on: [:create, :update]` -- Promotes the attachment, deletes replaced ones.
|
15
15
|
# * `after_commit on: [:destroy]` -- Deletes the attachment.
|
16
16
|
#
|
17
|
-
# Note that
|
18
|
-
#
|
19
|
-
#
|
17
|
+
# Note that ActiveRecord versions 3.x and 4.x have errors automatically
|
18
|
+
# silenced in hooks, which can make debugging more difficult, so it's
|
19
|
+
# recommended that you enable errors:
|
20
|
+
#
|
21
|
+
# # This is the default in ActiveRecord 5
|
22
|
+
# ActiveRecord::Base.raise_in_transactional_callbacks = true
|
23
|
+
#
|
24
|
+
# Also note that if your tests are wrapped in transactions, the
|
25
|
+
# `after_commit` callbacks won't get called, so in order to test uploading
|
26
|
+
# you should first disable these transactions for those tests.
|
20
27
|
#
|
21
28
|
# If you want to put some parts of this lifecycle into a background job, see
|
22
29
|
# the background_helpers plugin.
|
@@ -42,12 +49,11 @@ class Shrine
|
|
42
49
|
end
|
43
50
|
|
44
51
|
before_save do
|
45
|
-
#{@name}_attacher.save
|
52
|
+
#{@name}_attacher.save if #{@name}_attacher.attached?
|
46
53
|
end
|
47
54
|
|
48
55
|
after_commit on: [:create, :update] do
|
49
|
-
#{@name}_attacher.
|
50
|
-
#{@name}_attacher._promote
|
56
|
+
#{@name}_attacher.finalize if #{@name}_attacher.attached?
|
51
57
|
end
|
52
58
|
|
53
59
|
after_commit on: :destroy do
|
@@ -88,7 +88,7 @@ class Shrine
|
|
88
88
|
end
|
89
89
|
|
90
90
|
# If block is passed in, stores it to be called on deletion. Otherwise
|
91
|
-
# resolves data into objects and calls Shrine
|
91
|
+
# resolves data into objects and calls `Shrine#delete`.
|
92
92
|
def delete(data = nil, &block)
|
93
93
|
if block
|
94
94
|
shrine_class.opts[:background_delete] = block
|
@@ -98,11 +98,11 @@ class Shrine
|
|
98
98
|
record.id = record_id
|
99
99
|
|
100
100
|
name, phase = data["attachment"], data["phase"]
|
101
|
-
|
102
|
-
uploaded_file =
|
101
|
+
attacher = record.send("#{name}_attacher")
|
102
|
+
uploaded_file = attacher.uploaded_file(data["uploaded_file"])
|
103
103
|
context = {name: name.to_sym, record: record, phase: phase.to_sym}
|
104
104
|
|
105
|
-
|
105
|
+
attacher.store.delete(uploaded_file, context)
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
@@ -137,7 +137,7 @@ class Shrine
|
|
137
137
|
|
138
138
|
instance_exec(data, &background_delete)
|
139
139
|
else
|
140
|
-
super
|
140
|
+
super(uploaded_file, phase: phase)
|
141
141
|
end
|
142
142
|
end
|
143
143
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Shrine
|
2
|
+
module Plugins
|
3
|
+
# The default_url_options plugin allows you to specify URL options that
|
4
|
+
# will be applied by default for uploaded files of specified storages.
|
5
|
+
#
|
6
|
+
# plugin :default_url_options, store: {download: true}
|
7
|
+
#
|
8
|
+
# The default options are merged with options passed to `UploadedFile#url`,
|
9
|
+
# and the latter will always have precedence over default options.
|
10
|
+
module DefaultUrlOptions
|
11
|
+
def self.configure(uploader, **options)
|
12
|
+
uploader.opts[:default_url_options] = options
|
13
|
+
end
|
14
|
+
|
15
|
+
module FileMethods
|
16
|
+
def url(**options)
|
17
|
+
super(default_options.merge(options))
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def default_options
|
23
|
+
options = shrine_class.opts[:default_url_options]
|
24
|
+
options[storage_key.to_sym] || {}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
register_plugin(:default_url_options, DefaultUrlOptions)
|
30
|
+
end
|
31
|
+
end
|
@@ -64,6 +64,10 @@ class Shrine
|
|
64
64
|
uploader.opts[:mime_type_analyzer] = analyzer
|
65
65
|
end
|
66
66
|
|
67
|
+
# How many bytes we have to read to get the magic file header which
|
68
|
+
# contains the MIME type of the file.
|
69
|
+
MAGIC_NUMBER = 1024
|
70
|
+
|
67
71
|
module InstanceMethods
|
68
72
|
# If a Shrine::UploadedFile was given, it returns its MIME type, since
|
69
73
|
# that value was already determined by this analyzer. Otherwise it calls
|
@@ -86,16 +90,22 @@ class Shrine
|
|
86
90
|
# if it's a file, because even though the utility accepts standard
|
87
91
|
# input, it would mean that we have to read the whole file in memory.
|
88
92
|
def _extract_mime_type_with_file(io)
|
93
|
+
cmd = ["file", "--mime-type", "--brief"]
|
94
|
+
|
89
95
|
if io.respond_to?(:path)
|
90
|
-
mime_type, _ = Open3.capture2(
|
91
|
-
|
96
|
+
mime_type, _ = Open3.capture2(*cmd, io.path)
|
97
|
+
else
|
98
|
+
mime_type, _ = Open3.capture2(*cmd, "-", stdin_data: io.read(MAGIC_NUMBER), binmode: true)
|
99
|
+
io.rewind
|
92
100
|
end
|
101
|
+
|
102
|
+
mime_type.strip unless mime_type.empty?
|
93
103
|
end
|
94
104
|
|
95
105
|
# Uses the ruby-filemagic gem to magically extract the MIME type.
|
96
106
|
def _extract_mime_type_with_filemagic(io)
|
97
107
|
filemagic = FileMagic.new(FileMagic::MAGIC_MIME_TYPE)
|
98
|
-
data = io.read(
|
108
|
+
data = io.read(MAGIC_NUMBER); io.rewind
|
99
109
|
filemagic.buffer(data)
|
100
110
|
end
|
101
111
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require "roda"
|
2
2
|
|
3
3
|
require "json"
|
4
|
-
require "forwardable"
|
5
4
|
require "securerandom"
|
6
5
|
|
7
6
|
class Shrine
|
@@ -9,7 +8,7 @@ class Shrine
|
|
9
8
|
# The direct_upload plugin provides a Rack endpoint (implemented in [Roda])
|
10
9
|
# which you can use to implement AJAX uploads.
|
11
10
|
#
|
12
|
-
# plugin :direct_upload
|
11
|
+
# plugin :direct_upload
|
13
12
|
#
|
14
13
|
# This is how you could mount the endpoint in a Rails application:
|
15
14
|
#
|
@@ -20,35 +19,23 @@ class Shrine
|
|
20
19
|
#
|
21
20
|
# Note that you should mount a separate endpoint for each uploader that you
|
22
21
|
# want to use it with. This now gives your Ruby application a
|
23
|
-
# `POST /attachments/images/:storage/:name` route, which accepts a
|
24
|
-
# query parameter:
|
22
|
+
# `POST /attachments/images/:storage/:name` route, which accepts a `file`
|
23
|
+
# query parameter, and returns the uploaded file in JSON format:
|
25
24
|
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# <% end %>
|
37
|
-
#
|
38
|
-
# this is how we could hook up [jQuery-File-Upload] to our direct upload
|
39
|
-
# endpoint:
|
40
|
-
#
|
41
|
-
# $('[type="file"]').fileupload({
|
42
|
-
# url: '/attachments/images/cache/avatar',
|
43
|
-
# paramName: 'file',
|
44
|
-
# done: function(e, data) { $(this).prev().value(data.result) }
|
45
|
-
# });
|
25
|
+
# # POST /attachments/images/cache/avatar
|
26
|
+
# {
|
27
|
+
# "id": "43kewit94.jpg",
|
28
|
+
# "storage": "cache",
|
29
|
+
# "metadata": {
|
30
|
+
# "size": 384393,
|
31
|
+
# "filename": "nature.jpg",
|
32
|
+
# "mime_type": "image/jpeg"
|
33
|
+
# }
|
34
|
+
# }
|
46
35
|
#
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
50
|
-
# uploaded file JSON is written to the hidden field, and will be sent on
|
51
|
-
# form submit.
|
36
|
+
# There are many great JavaScript libraries for AJAX file uploads which can
|
37
|
+
# be hooked up to this endpoint, [jQuery-File-Upload] being the most
|
38
|
+
# popular one.
|
52
39
|
#
|
53
40
|
# ## Presigned
|
54
41
|
#
|
@@ -78,33 +65,29 @@ class Shrine
|
|
78
65
|
# The `url` is where the file needs to be uploaded to, and `fields` is
|
79
66
|
# additional data that needs to be send on the upload. The `fields.key`
|
80
67
|
# attribute is the location where the file will be uploaded to, it is
|
81
|
-
# generated randomly
|
82
|
-
#
|
83
|
-
# :extension
|
84
|
-
# : The extension of the file being uploaded (e.g ".jpg").
|
68
|
+
# generated randomly, but you can add an extension to it:
|
85
69
|
#
|
86
|
-
# :content_type
|
87
|
-
# : Sets the Content-Type header for the uploaded file on S3.
|
88
|
-
#
|
89
|
-
# Example:
|
90
|
-
#
|
91
|
-
# GET /cache/presign?content_type=image/jpeg
|
92
70
|
# GET /cache/presign?extension=.png
|
93
71
|
#
|
94
|
-
#
|
72
|
+
# If you want additional options to be passed to Storage::S3#presign, you
|
73
|
+
# can pass a block to `:presign` and return additional options:
|
95
74
|
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
75
|
+
# plugin :direct_upload, presign: ->(request) do
|
76
|
+
# {
|
77
|
+
# content_length_range: 0..(5*1024*1024), # limit the filesize to 5 MB
|
78
|
+
# success_action_redirect: "http://example.com/webhook",
|
79
|
+
# }
|
80
|
+
# end
|
100
81
|
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
# the option to nil:
|
82
|
+
# The yielded object is an instance of [`Roda::RodaRequest`] (a subclass of
|
83
|
+
# `Rack::Request`), which allows you to pass different options depending on
|
84
|
+
# the request. For example, you could accept a `content_type` query
|
85
|
+
# parameter and add it to options:
|
106
86
|
#
|
107
|
-
# plugin :direct_upload,
|
87
|
+
# plugin :direct_upload, presign: ->(request) do
|
88
|
+
# {content_type: request.params["content_type"]} if request.params["content_type"]
|
89
|
+
# end
|
90
|
+
# # Now you can do `GET /cache/presign?content_type=image/jpeg`
|
108
91
|
#
|
109
92
|
# ## Allowed storages
|
110
93
|
#
|
@@ -130,10 +113,14 @@ class Shrine
|
|
130
113
|
# [jQuery-File-Upload]: https://github.com/blueimp/jQuery-File-Upload
|
131
114
|
# [supports]: https://github.com/blueimp/jQuery-File-Upload/wiki/Options#progress
|
132
115
|
# ["accept" attribute]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-accept
|
116
|
+
# [`Roda::RodaRequest`]: http://roda.jeremyevans.net/rdoc/classes/Roda/RodaPlugins/Base/RequestMethods.html
|
133
117
|
module DirectUpload
|
134
|
-
def self.
|
118
|
+
def self.load_dependencies(uploader, *)
|
119
|
+
uploader.plugin :rack_file
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.configure(uploader, allowed_storages: [:cache], presign: nil, **)
|
135
123
|
uploader.opts[:direct_upload_allowed_storages] = allowed_storages
|
136
|
-
uploader.opts[:direct_upload_max_size] = max_size
|
137
124
|
uploader.opts[:direct_upload_presign] = presign
|
138
125
|
end
|
139
126
|
|
@@ -165,23 +152,23 @@ class Shrine
|
|
165
152
|
allow_storage!(storage_key)
|
166
153
|
@uploader = shrine_class.new(storage_key.to_sym)
|
167
154
|
|
168
|
-
|
169
|
-
|
170
|
-
|
155
|
+
unless presign
|
156
|
+
r.post ":name" do |name|
|
157
|
+
file = get_file
|
158
|
+
context = {name: name, phase: :cache}
|
171
159
|
|
172
|
-
|
173
|
-
|
160
|
+
json @uploader.upload(file, context)
|
161
|
+
end
|
162
|
+
else
|
163
|
+
r.get "presign" do
|
164
|
+
location = SecureRandom.hex(30) + r.params["extension"].to_s
|
165
|
+
options = presign.call(r) if presign.respond_to?(:call)
|
174
166
|
|
175
|
-
|
176
|
-
location = SecureRandom.hex(30).to_s + r.params["extension"].to_s
|
177
|
-
options = {}
|
178
|
-
options[:content_length_range] = 0..max_size if max_size
|
179
|
-
options[:content_type] = r.params["content_type"] if r.params["content_type"]
|
167
|
+
signature = @uploader.storage.presign(location, options || {})
|
180
168
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
end if presign?
|
169
|
+
json Hash[url: signature.url, fields: signature.fields]
|
170
|
+
end
|
171
|
+
end
|
185
172
|
end
|
186
173
|
end
|
187
174
|
|
@@ -201,18 +188,8 @@ class Shrine
|
|
201
188
|
def get_file
|
202
189
|
file = require_param!("file")
|
203
190
|
error! 400, "The \"file\" query parameter is not a file." if !(file.is_a?(Hash) && file.key?(:tempfile))
|
204
|
-
check_filesize!(file[:tempfile]) if max_size
|
205
|
-
|
206
|
-
RackFile.new(file)
|
207
|
-
end
|
208
191
|
|
209
|
-
|
210
|
-
def check_filesize!(file)
|
211
|
-
if file.size > max_size
|
212
|
-
file.delete
|
213
|
-
megabytes = max_size.to_f / 1024 / 1024
|
214
|
-
error! 413, "The file is too big (maximum size is #{megabytes} MB)."
|
215
|
-
end
|
192
|
+
RackFile::UploadedFile.new(file)
|
216
193
|
end
|
217
194
|
|
218
195
|
# Loudly requires the param.
|
@@ -235,38 +212,10 @@ class Shrine
|
|
235
212
|
shrine_class.opts[:direct_upload_allowed_storages]
|
236
213
|
end
|
237
214
|
|
238
|
-
def
|
239
|
-
shrine_class.opts[:direct_upload_max_size]
|
240
|
-
end
|
241
|
-
|
242
|
-
def presign?
|
215
|
+
def presign
|
243
216
|
shrine_class.opts[:direct_upload_presign]
|
244
217
|
end
|
245
218
|
end
|
246
|
-
|
247
|
-
# This is used to wrap the Rack hash into an IO-like object which Shrine
|
248
|
-
# can upload.
|
249
|
-
class RackFile
|
250
|
-
attr_reader :original_filename, :content_type
|
251
|
-
attr_accessor :tempfile
|
252
|
-
|
253
|
-
def initialize(tempfile:, filename: nil, type: nil, **)
|
254
|
-
@tempfile = tempfile
|
255
|
-
@original_filename = filename
|
256
|
-
@content_type = type
|
257
|
-
end
|
258
|
-
|
259
|
-
def path
|
260
|
-
@tempfile.path
|
261
|
-
end
|
262
|
-
|
263
|
-
def to_io
|
264
|
-
@tempfile
|
265
|
-
end
|
266
|
-
|
267
|
-
extend Forwardable
|
268
|
-
delegate Shrine::IO_METHODS.keys => :@tempfile
|
269
|
-
end
|
270
219
|
end
|
271
220
|
|
272
221
|
register_plugin(:direct_upload, DirectUpload)
|