| title | Upload Endpoint |
|---|
The upload_endpoint plugin provides a Rack endpoint which
accepts file uploads and forwards them to specified storage. On the client side
it's recommended to use Uppy for asynchronous uploads.
plugin :upload_endpointThe plugin adds a Shrine.upload_endpoint method which, given a storage
identifier, returns a Rack application that accepts multipart POST requests,
and uploads received files to the specified storage. You can run this Rack
application inside your app:
# config/routes.rb (Rails)
Rails.application.routes.draw do
# ...
mount ImageUploader.upload_endpoint(:cache) => "/images/upload"
endAsynchronous upload is typically meant to replace the caching phase in the
default synchronous workflow, so we want the uploads to go to temporary
(:cache) storage.
The above will create a POST /images/upload endpoint, which uploads the file
received in the file param using ImageUploader, and returns a JSON
representation of the uploaded file.
# POST /images/upload
{
"id": "43kewit94.jpg",
"storage": "cache",
"metadata": {
"size": 384393,
"filename": "nature.jpg",
"mime_type": "image/jpeg"
}
}This JSON string can now be assigned to an attachment attribute instead of a raw file. In a form it can be written to a hidden attachment field, and then it can be assigned as the attachment.
If you want to run additional code around the upload (such as authentication),
mounting the upload endpoint in your router might be limiting. You can instead
create a custom controller action and handle upload requests there using
Shrine.upload_response:
# config/routes.rb (Rails)
Rails.application.routes.draw do
# ...
post "/images/upload", to: "uploads#image"
end# app/controllers/uploads_controller.rb (Rails)
class UploadsController < ApplicationController
def image
# ... we can perform authentication here ...
set_rack_response ImageUploader.upload_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
endIt's good practice to limit the accepted filesize of uploaded files. You can do
that with the :max_size option:
plugin :upload_endpoint, max_size: 20*1024*1024 # 20 MBIf the uploaded file is larger than the specified value, a 413 Payload Too Large response will be returned.
You can pass additional uploader options via :upload_context:
plugin :upload_endpoint, upload_context: -> (request) do
{ location: "my-location" }
endNote that the uploader will not receive :record and :name values, as the
upload happens independently of a database record.
You can also customize the upload itself via the :upload option:
plugin :upload_endpoint, upload: -> (io, options, request) do
Shrine.upload(io, :cache, **options)
endYou can have the endpoint include the uploaded file URL in the response body
by specifying the :url option:
plugin :upload_endpoint, url: true
# or
plugin :upload_endpoint, url: { public: true }
# or
plugin :upload_endpoint, url: -> (uploaded_file, request) {
uploaded_file.url(**options)
}In this case the response body will be:
{
"data": { "id": "...", "storage": "...", "metadata": {...} },
"url": "https://example.com/path/to/file"
}The response returned by the endpoint can be customized via the
:rack_response option:
plugin :upload_endpoint, rack_response: -> (uploaded_file, request) do
body = { data: uploaded_file.data, url: uploaded_file.url }.to_json
[201, { "Content-Type" => "application/json" }, [body]]
endYou can override any of the options above when creating the endpoint/response:
Shrine.upload_endpoint(:cache, max_size: 20*1024*1024)
# or
Shrine.upload_response(:cache, request.env, max_size: 20*1024*1024)If you want the upload endpoint to verify the integrity of the uploaded file,
you can include the Content-MD5 header in the request filled with the
base64-encoded MD5 hash of the file that was calculated prior to the upload,
and the endpoint will automatically use it to verify the uploaded data.
If the checksums don't match, a 460 Checksum Mismatch response is returned.

