shrine 2.16.0 → 2.17.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 shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/doc/plugins/data_uri.md +9 -0
- data/doc/plugins/default_url.md +12 -0
- data/doc/plugins/derivation_endpoint.md +22 -5
- data/doc/plugins/determine_mime_type.md +34 -5
- data/doc/plugins/download_endpoint.md +9 -7
- data/doc/plugins/{metadata_attribues.md → metadata_attributes.md} +0 -0
- data/doc/plugins/remote_url.md +7 -1
- data/doc/release_notes/2.17.0.md +129 -0
- data/doc/storage/s3.md +3 -4
- data/lib/shrine/plugins/data_uri.rb +7 -2
- data/lib/shrine/plugins/default_url.rb +17 -5
- data/lib/shrine/plugins/derivation_endpoint.rb +43 -28
- data/lib/shrine/plugins/determine_mime_type.rb +20 -13
- data/lib/shrine/plugins/download_endpoint.rb +76 -84
- data/lib/shrine/plugins/metadata_attributes.rb +1 -1
- data/lib/shrine/plugins/parsed_json.rb +3 -2
- data/lib/shrine/plugins/presign_endpoint.rb +122 -118
- data/lib/shrine/plugins/processing.rb +1 -1
- data/lib/shrine/plugins/rack_response.rb +46 -15
- data/lib/shrine/plugins/remote_url.rb +2 -2
- data/lib/shrine/plugins/upload_endpoint.rb +109 -105
- data/lib/shrine/plugins/versions.rb +2 -1
- data/lib/shrine/storage/file_system.rb +1 -1
- data/lib/shrine/version.rb +1 -1
- data/shrine.gemspec +1 -2
- metadata +6 -19
@@ -11,10 +11,8 @@ class Shrine
|
|
11
11
|
# [doc/plugins/presign_endpoint.md]: https://github.com/shrinerb/shrine/blob/master/doc/plugins/presign_endpoint.md
|
12
12
|
module PresignEndpoint
|
13
13
|
def self.configure(uploader, opts = {})
|
14
|
-
uploader.opts[:
|
15
|
-
uploader.opts[:
|
16
|
-
uploader.opts[:presign_endpoint_presign] = opts.fetch(:presign, uploader.opts[:presign_endpoint_presign])
|
17
|
-
uploader.opts[:presign_endpoint_rack_response] = opts.fetch(:rack_response, uploader.opts[:presign_endpoint_rack_response])
|
14
|
+
uploader.opts[:presign_endpoint] ||= {}
|
15
|
+
uploader.opts[:presign_endpoint].merge!(opts)
|
18
16
|
end
|
19
17
|
|
20
18
|
module ClassMethods
|
@@ -26,142 +24,148 @@ class Shrine
|
|
26
24
|
# Additional options can be given to override the options given on
|
27
25
|
# plugin initialization.
|
28
26
|
def presign_endpoint(storage_key, **options)
|
29
|
-
|
30
|
-
shrine_class:
|
31
|
-
storage_key:
|
32
|
-
|
33
|
-
|
34
|
-
presign: opts[:presign_endpoint_presign],
|
35
|
-
rack_response: opts[:presign_endpoint_rack_response],
|
36
|
-
**options
|
27
|
+
Shrine::PresignEndpoint.new(
|
28
|
+
shrine_class: self,
|
29
|
+
storage_key: storage_key,
|
30
|
+
**opts[:presign_endpoint],
|
31
|
+
**options,
|
37
32
|
)
|
38
33
|
end
|
39
34
|
end
|
35
|
+
end
|
40
36
|
|
41
|
-
|
42
|
-
|
43
|
-
# JSON format.
|
44
|
-
class App
|
45
|
-
CONTENT_TYPE_JSON = "application/json; charset=utf-8"
|
46
|
-
CONTENT_TYPE_TEXT = "text/plain"
|
47
|
-
|
48
|
-
# Writes given options to instance variables.
|
49
|
-
def initialize(options)
|
50
|
-
options.each do |name, value|
|
51
|
-
instance_variable_set("@#{name}", value)
|
52
|
-
end
|
53
|
-
end
|
37
|
+
register_plugin(:presign_endpoint, PresignEndpoint)
|
38
|
+
end
|
54
39
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
40
|
+
# Rack application that accepts GET request to the root URL, calls
|
41
|
+
# `#presign` on the specified storage, and returns that information in
|
42
|
+
# JSON format.
|
43
|
+
class PresignEndpoint
|
44
|
+
CONTENT_TYPE_JSON = "application/json; charset=utf-8"
|
45
|
+
CONTENT_TYPE_TEXT = "text/plain"
|
46
|
+
|
47
|
+
# Writes given options to instance variables.
|
48
|
+
def initialize(options)
|
49
|
+
options.each do |name, value|
|
50
|
+
instance_variable_set("@#{name}", value)
|
51
|
+
end
|
52
|
+
end
|
63
53
|
|
64
|
-
|
65
|
-
|
66
|
-
|
54
|
+
# Accepts a Rack env hash, routes GET requests to the root URL, and
|
55
|
+
# returns a Rack response triple.
|
56
|
+
#
|
57
|
+
# If request isn't to the root URL, a `404 Not Found` response is
|
58
|
+
# returned. If request verb isn't GET, a `405 Method Not Allowed`
|
59
|
+
# response is returned.
|
60
|
+
def call(env)
|
61
|
+
request = Rack::Request.new(env)
|
67
62
|
|
68
|
-
|
69
|
-
|
63
|
+
status, headers, body = catch(:halt) do
|
64
|
+
error!(404, "Not Found") unless ["", "/"].include?(request.path_info)
|
65
|
+
error!(405, "Method Not Allowed") unless request.get?
|
70
66
|
|
71
|
-
|
67
|
+
handle_request(request)
|
68
|
+
end
|
72
69
|
|
73
|
-
|
74
|
-
end
|
70
|
+
headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s
|
75
71
|
|
76
|
-
|
72
|
+
[status, headers, body]
|
73
|
+
end
|
77
74
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
options = get_presign_options(request)
|
75
|
+
def inspect
|
76
|
+
"#<#{@shrine_class}::PresignEndpoint(:#{@storage_key})>"
|
77
|
+
end
|
78
|
+
alias to_s inspect
|
83
79
|
|
84
|
-
|
80
|
+
private
|
85
81
|
|
86
|
-
|
87
|
-
|
82
|
+
# Accepts a `Rack::Request` object, generates the presign, and returns a
|
83
|
+
# Rack response.
|
84
|
+
def handle_request(request)
|
85
|
+
location = get_presign_location(request)
|
86
|
+
options = get_presign_options(request)
|
88
87
|
|
89
|
-
|
90
|
-
# extension from the `filename` query parameter. If `:presign_location`
|
91
|
-
# option is given, calls that instead.
|
92
|
-
def get_presign_location(request)
|
93
|
-
if @presign_location
|
94
|
-
@presign_location.call(request)
|
95
|
-
else
|
96
|
-
extension = File.extname(request.params["filename"].to_s)
|
97
|
-
uploader.send(:generate_uid, nil) + extension
|
98
|
-
end
|
99
|
-
end
|
88
|
+
presign = generate_presign(location, options, request)
|
100
89
|
|
101
|
-
|
102
|
-
|
103
|
-
options = @presign_options
|
104
|
-
options = options.call(request) if options.respond_to?(:call)
|
105
|
-
options || {}
|
106
|
-
end
|
90
|
+
make_response(presign, request)
|
91
|
+
end
|
107
92
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
{ fields: {}, headers: {} }.merge(data.to_h)
|
120
|
-
else
|
121
|
-
Shrine.deprecation("Returning a custom object in Storage#presign is deprecated, presign_endpoint will not support it in Shrine 3. Storage#presign should return a Hash instead.")
|
122
|
-
|
123
|
-
url = data.url
|
124
|
-
fields = data.fields
|
125
|
-
headers = data.headers if data.respond_to?(:headers)
|
126
|
-
|
127
|
-
{ url: url, fields: fields.to_h, headers: headers.to_h }
|
128
|
-
end
|
129
|
-
end
|
93
|
+
# Generates the location using `Shrine#generate_uid`, and extracts the
|
94
|
+
# extension from the `filename` query parameter. If `:presign_location`
|
95
|
+
# option is given, calls that instead.
|
96
|
+
def get_presign_location(request)
|
97
|
+
if @presign_location
|
98
|
+
@presign_location.call(request)
|
99
|
+
else
|
100
|
+
extension = File.extname(request.params["filename"].to_s)
|
101
|
+
uploader.send(:generate_uid, nil) + extension
|
102
|
+
end
|
103
|
+
end
|
130
104
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
response = @rack_response.call(object, request)
|
138
|
-
else
|
139
|
-
response = [200, { "Content-Type" => CONTENT_TYPE_JSON }, [object.to_json]]
|
140
|
-
end
|
141
|
-
|
142
|
-
# prevent browsers from caching the response
|
143
|
-
response[1]["Cache-Control"] = "no-store" unless response[1].key?("Cache-Control")
|
144
|
-
|
145
|
-
response
|
146
|
-
end
|
105
|
+
# Calls `:presign_options` option block if given.
|
106
|
+
def get_presign_options(request)
|
107
|
+
options = @presign_options
|
108
|
+
options = options.call(request) if options.respond_to?(:call)
|
109
|
+
options || {}
|
110
|
+
end
|
147
111
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
112
|
+
# Calls `#presign` on the storage, and returns the `url`, `fields`, and
|
113
|
+
# `headers` information in a serialializable format. If `:presign`
|
114
|
+
# option is given, calls that instead of calling `#presign`.
|
115
|
+
def generate_presign(location, options, request)
|
116
|
+
if @presign
|
117
|
+
data = @presign.call(location, options, request)
|
118
|
+
else
|
119
|
+
data = storage.presign(location, options)
|
120
|
+
end
|
152
121
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
122
|
+
if data.respond_to?(:to_h)
|
123
|
+
{ fields: {}, headers: {} }.merge(data.to_h)
|
124
|
+
else
|
125
|
+
Shrine.deprecation("Returning a custom object in Storage#presign is deprecated, presign_endpoint will not support it in Shrine 3. Storage#presign should return a Hash instead.")
|
157
126
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
127
|
+
url = data.url
|
128
|
+
fields = data.fields
|
129
|
+
headers = data.headers if data.respond_to?(:headers)
|
130
|
+
|
131
|
+
{ url: url, fields: fields.to_h, headers: headers.to_h }
|
162
132
|
end
|
163
133
|
end
|
164
134
|
|
165
|
-
|
135
|
+
# Transforms the presign hash into a JSON response. It returns a Rack
|
136
|
+
# response triple - an array consisting of a status number, hash of
|
137
|
+
# headers, and a body enumerable. If `:rack_response` option is given,
|
138
|
+
# calls that instead.
|
139
|
+
def make_response(object, request)
|
140
|
+
if @rack_response
|
141
|
+
response = @rack_response.call(object, request)
|
142
|
+
else
|
143
|
+
response = [200, { "Content-Type" => CONTENT_TYPE_JSON }, [object.to_json]]
|
144
|
+
end
|
145
|
+
|
146
|
+
# prevent browsers from caching the response
|
147
|
+
response[1]["Cache-Control"] = "no-store" unless response[1].key?("Cache-Control")
|
148
|
+
|
149
|
+
response
|
150
|
+
end
|
151
|
+
|
152
|
+
# Used for early returning an error response.
|
153
|
+
def error!(status, message)
|
154
|
+
throw :halt, [status, { "Content-Type" => CONTENT_TYPE_TEXT }, [message]]
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns the uploader around the specified storage.
|
158
|
+
def uploader
|
159
|
+
@shrine_class.new(@storage_key)
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns the storage object.
|
163
|
+
def storage
|
164
|
+
@shrine_class.find_storage(@storage_key)
|
165
|
+
end
|
166
166
|
end
|
167
|
+
|
168
|
+
# backwards compatibility
|
169
|
+
Plugins::PresignEndpoint.const_set(:App, PresignEndpoint)
|
170
|
+
Plugins::PresignEndpoint.deprecate_constant(:App)
|
167
171
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require "rack"
|
4
4
|
require "content_disposition"
|
5
|
+
require "digest"
|
5
6
|
|
6
7
|
class Shrine
|
7
8
|
module Plugins
|
@@ -49,18 +50,49 @@ class Shrine
|
|
49
50
|
# metadata. Also returns the correct "Content-Range" header on ranged
|
50
51
|
# requests.
|
51
52
|
def rack_headers(filename: nil, type: nil, disposition: "inline", range: false)
|
52
|
-
|
53
|
-
|
53
|
+
{
|
54
|
+
"Content-Length" => content_length(range),
|
55
|
+
"Content-Type" => content_type(type),
|
56
|
+
"Content-Disposition" => content_disposition(disposition, filename),
|
57
|
+
"Content-Range" => content_range(range),
|
58
|
+
"Accept-Ranges" => accept_ranges(range),
|
59
|
+
"ETag" => etag,
|
60
|
+
}.compact
|
61
|
+
end
|
62
|
+
|
63
|
+
# Value for the "Content-Length" header.
|
64
|
+
def content_length(range)
|
65
|
+
length = range ? range.size : file.size
|
66
|
+
length.to_s if length
|
67
|
+
end
|
68
|
+
|
69
|
+
# Value for the "Content-Type" header.
|
70
|
+
def content_type(type)
|
71
|
+
type || file.mime_type || Rack::Mime.mime_type(".#{file.extension}", nil)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Value for the "Content-Disposition" header.
|
75
|
+
def content_disposition(disposition, filename)
|
54
76
|
filename ||= file.original_filename || file.id.split("/").last
|
55
77
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
78
|
+
ContentDisposition.format(disposition: disposition, filename: filename)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Value for the "Content-Range" header.
|
82
|
+
def content_range(range)
|
83
|
+
"bytes #{range.begin}-#{range.end}/#{file.size}" if range
|
84
|
+
end
|
85
|
+
|
86
|
+
# Value for the "Accept-Ranges" header.
|
87
|
+
def accept_ranges(range)
|
88
|
+
"bytes" unless range == false
|
89
|
+
end
|
90
|
+
|
91
|
+
# Value for the "ETag" header.
|
92
|
+
def etag
|
93
|
+
digest = Digest::SHA256.hexdigest("#{file.shrine_class}-#{file.storage_key}-#{file.id}")
|
62
94
|
|
63
|
-
|
95
|
+
%(W/"#{digest.byteslice(0, 32)}")
|
64
96
|
end
|
65
97
|
|
66
98
|
# Returns an object that responds to #each and #close, which yields
|
@@ -79,10 +111,6 @@ class Shrine
|
|
79
111
|
|
80
112
|
ranges.first if ranges && ranges.one?
|
81
113
|
end
|
82
|
-
|
83
|
-
def content_disposition(disposition, filename)
|
84
|
-
ContentDisposition.format(disposition: disposition, filename: filename)
|
85
|
-
end
|
86
114
|
end
|
87
115
|
|
88
116
|
# Implements the interface of a Rack response body object.
|
@@ -128,9 +156,12 @@ class Shrine
|
|
128
156
|
read_chunks do |chunk|
|
129
157
|
chunk_range = bytes_read..(bytes_read + chunk.bytesize - 1)
|
130
158
|
|
131
|
-
if chunk_range.begin
|
159
|
+
if chunk_range.begin > range.end
|
160
|
+
# no more chunks will match
|
161
|
+
return
|
162
|
+
elsif chunk_range.begin >= range.begin && chunk_range.end <= range.end
|
132
163
|
yield chunk
|
133
|
-
elsif chunk_range.end >= range.begin
|
164
|
+
elsif chunk_range.end >= range.begin && chunk_range.begin <= range.end
|
134
165
|
requested_range_begin = [chunk_range.begin, range.begin].max - bytes_read
|
135
166
|
requested_range_end = [chunk_range.end, range.end].min - bytes_read
|
136
167
|
|
@@ -36,7 +36,7 @@ class Shrine
|
|
36
36
|
# Downloads the remote file and assigns it. If download failed, sets
|
37
37
|
# the error message and assigns the url to an instance variable so that
|
38
38
|
# it shows up in the form.
|
39
|
-
def assign_remote_url(url, downloader: {})
|
39
|
+
def assign_remote_url(url, downloader: {}, **options)
|
40
40
|
return if url == "" || url.nil?
|
41
41
|
|
42
42
|
begin
|
@@ -46,7 +46,7 @@ class Shrine
|
|
46
46
|
end
|
47
47
|
|
48
48
|
if downloaded_file
|
49
|
-
assign(downloaded_file)
|
49
|
+
assign(downloaded_file, **options)
|
50
50
|
else
|
51
51
|
message = download_error_message(url, download_error)
|
52
52
|
errors.replace [message]
|
@@ -16,10 +16,8 @@ class Shrine
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def self.configure(uploader, opts = {})
|
19
|
-
uploader.opts[:
|
20
|
-
uploader.opts[:
|
21
|
-
uploader.opts[:upload_endpoint_upload] = opts.fetch(:upload, uploader.opts[:upload_endpoint_upload])
|
22
|
-
uploader.opts[:upload_endpoint_rack_response] = opts.fetch(:rack_response, uploader.opts[:upload_endpoint_rack_response])
|
19
|
+
uploader.opts[:upload_endpoint] ||= {}
|
20
|
+
uploader.opts[:upload_endpoint].merge!(opts)
|
23
21
|
end
|
24
22
|
|
25
23
|
module ClassMethods
|
@@ -31,132 +29,138 @@ class Shrine
|
|
31
29
|
# Additional options can be given to override the options given on
|
32
30
|
# plugin initialization.
|
33
31
|
def upload_endpoint(storage_key, **options)
|
34
|
-
|
35
|
-
shrine_class:
|
36
|
-
storage_key:
|
37
|
-
|
38
|
-
|
39
|
-
upload: opts[:upload_endpoint_upload],
|
40
|
-
rack_response: opts[:upload_endpoint_rack_response],
|
41
|
-
**options
|
32
|
+
Shrine::UploadEndpoint.new(
|
33
|
+
shrine_class: self,
|
34
|
+
storage_key: storage_key,
|
35
|
+
**opts[:upload_endpoint],
|
36
|
+
**options,
|
42
37
|
)
|
43
38
|
end
|
44
39
|
end
|
40
|
+
end
|
45
41
|
|
46
|
-
|
47
|
-
|
48
|
-
# information in JSON format.
|
49
|
-
class App
|
50
|
-
CONTENT_TYPE_JSON = "application/json; charset=utf-8"
|
51
|
-
CONTENT_TYPE_TEXT = "text/plain"
|
52
|
-
|
53
|
-
# Writes given options to instance variables.
|
54
|
-
def initialize(options)
|
55
|
-
options.each do |name, value|
|
56
|
-
instance_variable_set("@#{name}", value)
|
57
|
-
end
|
58
|
-
end
|
42
|
+
register_plugin(:upload_endpoint, UploadEndpoint)
|
43
|
+
end
|
59
44
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
45
|
+
# Rack application that accepts multipart POST request to the root URL,
|
46
|
+
# calls `#upload` with the uploaded file, and returns the uploaded file
|
47
|
+
# information in JSON format.
|
48
|
+
class UploadEndpoint
|
49
|
+
CONTENT_TYPE_JSON = "application/json; charset=utf-8"
|
50
|
+
CONTENT_TYPE_TEXT = "text/plain"
|
51
|
+
|
52
|
+
# Writes given options to instance variables.
|
53
|
+
def initialize(options)
|
54
|
+
options.each do |name, value|
|
55
|
+
instance_variable_set("@#{name}", value)
|
56
|
+
end
|
57
|
+
end
|
68
58
|
|
69
|
-
|
70
|
-
|
71
|
-
|
59
|
+
# Accepts a Rack env hash, routes POST requests to the root URL, and
|
60
|
+
# returns a Rack response triple.
|
61
|
+
#
|
62
|
+
# If request isn't to the root URL, a `404 Not Found` response is
|
63
|
+
# returned. If request verb isn't GET, a `405 Method Not Allowed`
|
64
|
+
# response is returned.
|
65
|
+
def call(env)
|
66
|
+
request = Rack::Request.new(env)
|
72
67
|
|
73
|
-
|
74
|
-
|
68
|
+
status, headers, body = catch(:halt) do
|
69
|
+
error!(404, "Not Found") unless ["", "/"].include?(request.path_info)
|
70
|
+
error!(405, "Method Not Allowed") unless request.post?
|
75
71
|
|
76
|
-
|
72
|
+
handle_request(request)
|
73
|
+
end
|
77
74
|
|
78
|
-
|
79
|
-
end
|
75
|
+
headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s
|
80
76
|
|
81
|
-
|
77
|
+
[status, headers, body]
|
78
|
+
end
|
82
79
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
context = get_context(request)
|
80
|
+
def inspect
|
81
|
+
"#<#{@shrine_class}::UploadEndpoint(:#{@storage_key})>"
|
82
|
+
end
|
83
|
+
alias to_s inspect
|
88
84
|
|
89
|
-
|
85
|
+
private
|
90
86
|
|
91
|
-
|
92
|
-
|
87
|
+
# Accepts a `Rack::Request` object, uploads the file, and returns a Rack
|
88
|
+
# response.
|
89
|
+
def handle_request(request)
|
90
|
+
io = get_io(request)
|
91
|
+
context = get_context(request)
|
93
92
|
|
94
|
-
|
95
|
-
# IO-like object that can be passed to `Shrine#upload`.
|
96
|
-
def get_io(request)
|
97
|
-
file = request.params["file"]
|
93
|
+
uploaded_file = upload(io, context, request)
|
98
94
|
|
99
|
-
|
100
|
-
|
101
|
-
error!(413, "Upload Too Large") if @max_size && file[:tempfile].size > @max_size
|
95
|
+
make_response(uploaded_file, request)
|
96
|
+
end
|
102
97
|
|
103
|
-
|
98
|
+
# Retrieves the "file" multipart request parameter, and returns an
|
99
|
+
# IO-like object that can be passed to `Shrine#upload`.
|
100
|
+
def get_io(request)
|
101
|
+
file = request.params["file"]
|
104
102
|
|
105
|
-
|
106
|
-
|
103
|
+
error!(400, "Upload Not Found") if file.nil?
|
104
|
+
error!(400, "Upload Not Valid") unless file.is_a?(Hash) && file[:tempfile]
|
105
|
+
error!(413, "Upload Too Large") if @max_size && file[:tempfile].size > @max_size
|
107
106
|
|
108
|
-
|
109
|
-
# keys, which is to be passed to `Shrine#upload`. Calls
|
110
|
-
# `:upload_context` option if given.
|
111
|
-
def get_context(request)
|
112
|
-
context = { action: :upload, phase: :upload, request: request }
|
113
|
-
context.merge! @upload_context.call(request) if @upload_context
|
114
|
-
context
|
115
|
-
end
|
107
|
+
verify_checksum!(file[:tempfile], request.env["HTTP_CONTENT_MD5"]) if request.env["HTTP_CONTENT_MD5"]
|
116
108
|
|
117
|
-
|
118
|
-
|
119
|
-
# that instead.
|
120
|
-
def upload(io, context, request)
|
121
|
-
if @upload
|
122
|
-
@upload.call(io, context, request)
|
123
|
-
else
|
124
|
-
uploader.upload(io, context)
|
125
|
-
end
|
126
|
-
end
|
109
|
+
@shrine_class.rack_file(file)
|
110
|
+
end
|
127
111
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
[200, { "Content-Type" => CONTENT_TYPE_JSON }, [object.to_json]]
|
137
|
-
end
|
138
|
-
end
|
112
|
+
# Returns a hash of information containing `:action` and `:request`
|
113
|
+
# keys, which is to be passed to `Shrine#upload`. Calls
|
114
|
+
# `:upload_context` option if given.
|
115
|
+
def get_context(request)
|
116
|
+
context = { action: :upload, phase: :upload, request: request }
|
117
|
+
context.merge! @upload_context.call(request) if @upload_context
|
118
|
+
context
|
119
|
+
end
|
139
120
|
|
140
|
-
|
141
|
-
|
142
|
-
|
121
|
+
# Calls `Shrine#upload` with the given IO and context, and returns a
|
122
|
+
# `Shrine::UploadedFile` object. If `:upload` option is given, calls
|
123
|
+
# that instead.
|
124
|
+
def upload(io, context, request)
|
125
|
+
if @upload
|
126
|
+
@upload.call(io, context, request)
|
127
|
+
else
|
128
|
+
uploader.upload(io, context)
|
129
|
+
end
|
130
|
+
end
|
143
131
|
|
144
|
-
|
145
|
-
|
146
|
-
|
132
|
+
# Transforms the uploaded file object into a JSON response. It returns
|
133
|
+
# a Rack response triple - an array consisting of a status number, hash
|
134
|
+
# of headers, and a body enumerable. If a `:rack_response` option is
|
135
|
+
# given, calls that instead.
|
136
|
+
def make_response(object, request)
|
137
|
+
if @rack_response
|
138
|
+
@rack_response.call(object, request)
|
139
|
+
else
|
140
|
+
[200, { "Content-Type" => CONTENT_TYPE_JSON }, [object.to_json]]
|
141
|
+
end
|
142
|
+
end
|
147
143
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
end
|
144
|
+
# Verifies the provided checksum against the received file.
|
145
|
+
def verify_checksum!(file, provided_checksum)
|
146
|
+
error!(400, "The Content-MD5 you specified was invalid") if provided_checksum.length != 24
|
152
147
|
|
153
|
-
|
154
|
-
|
155
|
-
@shrine_class.new(@storage_key)
|
156
|
-
end
|
157
|
-
end
|
148
|
+
calculated_checksum = Digest::MD5.file(file.path).base64digest
|
149
|
+
error!(460, "The Content-MD5 you specified did not match what was recieved") if provided_checksum != calculated_checksum
|
158
150
|
end
|
159
151
|
|
160
|
-
|
152
|
+
# Used for early returning an error response.
|
153
|
+
def error!(status, message)
|
154
|
+
throw :halt, [status, { "Content-Type" => CONTENT_TYPE_TEXT }, [message]]
|
155
|
+
end
|
156
|
+
|
157
|
+
# Returns the uploader around the specified storage.
|
158
|
+
def uploader
|
159
|
+
@shrine_class.new(@storage_key)
|
160
|
+
end
|
161
161
|
end
|
162
|
+
|
163
|
+
# backwards compatibility
|
164
|
+
Plugins::UploadEndpoint.const_set(:App, UploadEndpoint)
|
165
|
+
Plugins::UploadEndpoint.deprecate_constant(:App)
|
162
166
|
end
|