class Shrine::UploadEndpoint
Rack application that accepts multipart POST request to the root URL, calls ‘#upload` with the uploaded file, and returns the uploaded file information in JSON format.
Constants
- CONTENT_TYPE_JSON
- CONTENT_TYPE_TEXT
Public Class Methods
Writes given options to instance variables.
# File lib/shrine/plugins/upload_endpoint.rb, line 72 def initialize(options) options.each do |name, value| instance_variable_set("@#{name}", value) end end
Public Instance Methods
Accepts a Rack env hash, routes POST requests to the root URL, and returns a Rack response triple.
If request isn’t to the root URL, a ‘404 Not Found` response is returned. If request verb isn’t GET, a ‘405 Method Not Allowed` response is returned.
# File lib/shrine/plugins/upload_endpoint.rb, line 84 def call(env) request = Rack::Request.new(env) status, headers, body = catch(:halt) do error!(404, "Not Found") unless ["", "/"].include?(request.path_info) error!(405, "Method Not Allowed") unless request.post? handle_request(request) end headers = Rack::Headers[headers] if Rack.release >= "3" headers["Content-Length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s : body.map(&:bytesize).inject(0, :+).to_s [status, headers, body] end
# File lib/shrine/plugins/upload_endpoint.rb, line 101 def inspect "#<#{@shrine_class}::UploadEndpoint(:#{@storage_key})>" end
Private Instance Methods
Used for early returning an error response.
# File lib/shrine/plugins/upload_endpoint.rb, line 214 def error!(status, message) throw :halt, [status, { "Content-Type" => CONTENT_TYPE_TEXT }, [message]] end
Returns a hash of information containing ‘:action` and `:request` keys, which is to be passed to `Shrine#upload`. Calls `:upload_context` option if given.
# File lib/shrine/plugins/upload_endpoint.rb, line 153 def get_context(request) context = { action: :upload } context.merge! @upload_context.call(request) if @upload_context context end
Retrieves the upload from the request and verifies it.
# File lib/shrine/plugins/upload_endpoint.rb, line 120 def get_io(request) file = get_multipart_upload(request) verify_size!(file, request) verify_checksum!(file, request) file end
Retrieves the file from “file” or “files[]” multipart POST param, and converts it into an IO-like object that can be passed to ‘Shrine#upload`.
# File lib/shrine/plugins/upload_endpoint.rb, line 131 def get_multipart_upload(request) if request.params.key?("file") value = request.params["file"] elsif request.params["files"].is_a?(Array) error!(400, "Too Many Files") if request.params["files"].count > 1 value = request.params["files"].first end error!(400, "Upload Not Found") if value.nil? if value.is_a?(Hash) && value[:tempfile] @shrine_class.rack_file(value) elsif %i[read rewind eof? close].all? { |m| value.respond_to?(m) } value else error!(400, "Upload Not Valid") end end
Accepts a ‘Rack::Request` object, uploads the file, and returns a Rack response.
# File lib/shrine/plugins/upload_endpoint.rb, line 110 def handle_request(request) io = get_io(request) context = get_context(request) uploaded_file = upload(io, context, request) make_response(uploaded_file, request) end
Transforms the uploaded file object into a JSON response. It returns a Rack response triple - an array consisting of a status number, hash of headers, and a body enumerable. If a ‘:rack_response` option is given, calls that instead.
# File lib/shrine/plugins/upload_endpoint.rb, line 174 def make_response(uploaded_file, request) if @rack_response @rack_response.call(uploaded_file, request) else if @url url = resolve_url(uploaded_file, request) body = { data: uploaded_file, url: url }.to_json else body = uploaded_file.to_json end [200, { "Content-Type" => CONTENT_TYPE_JSON }, [body]] end end
# File lib/shrine/plugins/upload_endpoint.rb, line 189 def resolve_url(uploaded_file, request) case @url when true then uploaded_file.url when Hash then uploaded_file.url(**@url) else @url.call(uploaded_file, request) end end
Calls ‘Shrine#upload` with the given IO and context, and returns a `Shrine::UploadedFile` object. If `:upload` option is given, calls that instead.
# File lib/shrine/plugins/upload_endpoint.rb, line 162 def upload(io, context, request) if @upload @upload.call(io, context, request) else uploader.upload(io, **context) end end
Returns the uploader around the specified storage.
# File lib/shrine/plugins/upload_endpoint.rb, line 219 def uploader @shrine_class.new(@storage_key) end
Verifies the provided checksum against the received file.
# File lib/shrine/plugins/upload_endpoint.rb, line 202 def verify_checksum!(file, request) return unless request.env.key?("HTTP_CONTENT_MD5") provided_checksum = request.env["HTTP_CONTENT_MD5"] error!(400, "The Content-MD5 you specified was invalid") if provided_checksum.length != 24 calculated_checksum = Digest::MD5.file(file.path).base64digest error!(460, "The Content-MD5 you specified did not match what was recieved") if provided_checksum != calculated_checksum end
# File lib/shrine/plugins/upload_endpoint.rb, line 197 def verify_size!(file, request) error!(413, "Upload Too Large") if @max_size && file.size > @max_size end