Skip to main content

Download Endpoint

The download_endpoint plugin provides a Rack app for downloading uploaded files from specified storages. This can be useful when files from your storage isn't accessible over URL (e.g. database storages) or if you want to authenticate your downloads.

Global Endpoint

You can configure the plugin with the path prefix which the endpoint will be mounted on.

Shrine.plugin :download_endpoint, prefix: "attachments"

The plugin adds a Shrine.download_endpoint method which returns a Rack application that handles downloads using the rack_response plugin. You can run this Rack app inside your app on the prefix that you specified:

# config/routes.rb (Rails)
Rails.application.routes.draw do
  # ...
  mount Shrine.download_endpoint => "/attachments"
end

Links to the download endpoint are generated by calling UploadedFile#download_url instead of the usual UploadedFile#url.

uploaded_file.download_url #=> "/attachments/eyJpZCI6ImFkdzlyeTM..."

Endpoint via Uploader

You can also configure the plugin in the uploader directly - just make sure to mount it via your Uploader-class.

class ImageUploader  < Shrine
  plugin :download_endpoint, prefix: "images"
end
# config/routes.rb (Rails)
Rails.application.routes.draw do
  # ...
  mount ImageUploader.download_endpoint => "/images"
end

Hint: For shrine versions 2.x -> ensure that you don't include the plugin twice (globally and in your uploader class - see #408)

Calling from a controller

If you want to run additional code around the download (such as authentication), mounting the download endpoint in your router might be limiting. You can instead create a custom controller action and handle download requests there using Shrine.download_response:

# config/routes.rb (Rails)
Rails.application.routes.draw do
  # ...
  get "/attachments/*rest", to: "downloads#image"
end
# app/controllers/downloads_controller.rb (Rails)
class DownloadsController < ApplicationController
  def image
    # ... we can perform authentication here ...
    set_rack_response ImageUploader.download_response(request.env)
  end

  private

  def set_rack_response((status, headers, body))
    self.status = status
    self.headers.merge!(headers)
    self.response_body = body
  end
end

If you want to create an endpoint with a custom path, you can use the rack_response plugin directly, which this plugin uses internally.

Host

You can specify download URL host via the :host plugin option:

plugin :download_endpoint, host: "http://example.com"

or by passing :host to UploadedFile#download_url:

uploaded_file.download_url(host: "http://example.com")
#=> "http//example.com/attachments/eyJpZCI6ImFkdzlyeTM..."

Download options

If you want to pass additional options to Storage#open, you can do so via :download_options:

plugin :download_endpoint, download_options: {
  sse_customer_algorithm: "AES256",
  sse_customer_key:       "secret_key",
  sse_customer_key_md5:   "secret_key_md5",
}

You can also specify a proc to generate download options dynamically:

plugin :download_endpoint, download_options: -> (uploaded_file, request) {
  {
    sse_customer_algorithm: "AES256",
    sse_customer_key:       "secret_key",
    sse_customer_key_md5:   "secret_key_md5",
  }
}

Performance considerations

Streaming files through the app might impact the request throughput, depending on the web server you're using. So it's recommended to use a CDN, which can be set via the :host option.

Alternatively, you can have the endpoint redirect to the direct file URL:

plugin :download_endpoint, redirect: true

You can also specify a proc to generate your own redirect URL:

plugin :download_endpoint, redirect: -> (uploaded_file, request) do
  uploaded_file.url(public: true)
end

Ad-hoc options

You can override any of the options above when creating the endpoint:

Shrine.download_endpoint(disposition: "attachment")

Plugin options

NameDescriptionDefault
:dispositionWhether browser should render the file inline or download it as an attachmentinline
:download_optionsHash of storage-specific options passed to Storage#open{}
:hostURL host that will be added to download URLsnil
:prefixPath prefix prepended to download URLsnil
:redirectWhether to redirect to uploaded files on the storagefalse