Presign Endpoint
The presign_endpoint
plugin provides a Rack endpoint
which generates the URL, fields, and headers that can be used to upload files
directly to a storage service. On the client side it's recommended to use
Uppy for asynchronous uploads. Storage services that support direct uploads
include Amazon S3, Google Cloud Storage, Microsoft Azure Storage and
more.
plugin :presign_endpoint
Setup
The plugin adds a Shrine.presign_endpoint
method which, given a storage
identifier, returns a Rack application that accepts GET requests and generates
a presign for the specified storage. You can run this Rack application inside
your app:
# config/routes.rb (Rails)
Rails.application.routes.draw do
# ...
mount ImageUploader.presign_endpoint(:cache) => "/images/presign"
end
Asynchronous upload is typically meant to replace the caching phase in the
default synchronous workflow, so we want to generate parameters for uploads to
the temporary (:cache
) storage.
The above will create a GET /images/presign
endpoint, which calls #presign
on the storage and returns the HTTP verb, URL, params, and headers needed for a
single upload directly to the storage service, in JSON format.
# GET /images/presign
{
"method": "post",
"url": "https://my-bucket.s3-eu-west-1.amazonaws.com",
"fields": {
"key": "b7d575850ba61b44c8a9ff889dfdb14d88cdc25f8dd121004c8",
"policy": "eyJleHBpcmF0aW9uIjoiMjAxNS0QwMToxMToyOVoiLCJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJ...",
"x-amz-credential": "AKIAIJF55TMZYT6Q/20151024/eu-west-1/s3/aws4_request",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-date": "20151024T001129Z",
"x-amz-signature": "c1eb634f83f96b69bd675f535b3ff15ae184b102fcba51e4db5f4959b4ae26f4"
},
"headers": {}
}
Calling from a controller
If you want to run additional code around the presign (such as authentication),
mounting the presign endpoint in your router might be limiting. You can instead
create a custom controller action and handle presign requests there using
Shrine.presign_response
:
# config/routes.rb (Rails)
Rails.application.routes.draw do
# ...
get "/images/presign", to: "presigns#image"
end
# app/controllers/presigns_controller.rb (Rails)
class PresignsController < ApplicationController
def image
# ... we can perform authentication here ...
set_rack_response ImageUploader.presign_response(:cache, request.env)
end
private
def set_rack_response((status, headers, body))
self.status = status
self.headers.merge!(headers)
self.response_body = body
end
end
Location
By default the generated location won't have any file extension, but you can
specify one by sending the filename
query parameter:
GET /images/presign?filename=nature.jpg
It's also possible to customize how the presign location is generated:
plugin :presign_endpoint, presign_location: -> (request) do
"#{SecureRandom.hex}/#{request.params["filename"]}"
end
Options
Some storages accept additional presign options, which you can pass in via
:presign_options
, here is an example for S3 storage:
plugin :presign_endpoint, presign_options: -> (request) do
# Uppy will send the "filename" and "type" query parameters
filename = request.params["filename"]
type = request.params["type"]
{
content_length_range: 0..(10*1024*1024), # limit filesize to 10MB
content_disposition: ContentDisposition.inline(filename), # download with original filename
content_type: type, # set correct content type
}
end
The example above uses the content_disposition gem to correctly format the
Content-Disposition
header value.
The :presign_options
can be a Proc or a Hash.
Presign
You can also customize how the presign itself is generated via the :presign
option:
plugin :presign_endpoint, presign: -> (id, options, request) do
# return a Hash with :method, :url, :fields, and :headers keys
Shrine.storages[:cache].presign(id, options)
end
Response
The response returned by the endpoint can be customized via the
:rack_response
option:
plugin :presign_endpoint, rack_response: -> (data, request) do
body = { endpoint: data[:url], params: data[:fields], headers: data[:headers] }.to_json
[201, { "Content-Type" => "application/json" }, [body]]
end
Ad-hoc options
You can override any of the options above when creating the endpoint/response:
Shrine.presign_endpoint(:cache, presign_location: "${filename}")
# or
Shrine.presign_response(:cache, env, presign_location: "${filename}")