Shrine

Shrine

  • Guides
  • Plugins
  • External
  • Discourse
  • GitHub
  • Wiki

›Flow

Attachment

  • Active Record
  • Column
  • Entity
  • Model
  • Sequel

Flow

  • Backgrounding
  • Presign Endpoint
  • Upload Endpoint

Processing

  • Derivation Endpoint
  • Derivatives

Source

  • Data URI
  • Rack File
  • Remote URL

Validation

  • Remove Invalid
  • Validation
  • Validation Helpers

Metadata

  • Add Metadata
  • Determine MIME Type
  • Infer Extension
  • Metadata Attributes
  • Refresh Metadata
  • Restore Cached Data
  • Signature
  • Store Dimensions
  • Type Predicates

Downloading

  • Download Endpoint
  • Rack Response
  • Tempfile

Form

  • Cached Attachment Data
  • Form Assign
  • Remove Attachment

Settings

  • Default Storage
  • Default URL
  • Dynamic Storage
  • Multi Cache
  • Pretty Location
  • Upload Options
  • URL Options

Other

  • Atomic Helpers
  • Included
  • Instrumentation
  • Keep Files
  • Mirroring
Edit

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_endpoint

Setup

The 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"
end

Asynchronous 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.

Calling from a controller

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
end

Limiting filesize

It'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 MB 

If the uploaded file is larger than the specified value, a 413 Payload Too Large response will be returned.

Uploader options

You can pass additional uploader options via :upload_context:

plugin :upload_endpoint, upload_context: -> (request) do
  { location: "my-location" }
end

Note that the uploader will not receive :record and :name values, as the upload happens independently of a database record.

Upload

You can also customize the upload itself via the :upload option:

plugin :upload_endpoint, upload: -> (io, options, request) do
  Shrine.upload(io, :cache, **options)
end

URL

You 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"
}

Response

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]]
end

Ad-hoc options

You 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)

Checksum

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.

← Presign EndpointDerivation Endpoint →
  • Setup
  • Calling from a controller
  • Limiting filesize
  • Uploader options
  • Upload
  • URL
  • Response
  • Ad-hoc options
  • Checksum
Shrine
Docs
GuidesPluginsExternalContributing
Community
DiscourseStack Overflow
More
BlogGitHubStar
Follow @shrine_rb
Copyright © 2022 Janko Marohnić