Remote URL
The remote_url plugin allows you to attach files from a remote
location.
plugin :remote_url, max_size: 20*1024*1024Usage
The plugin will add the #<name>_remote_url writer to your model, which
downloads the remote file and uploads it to temporary storage.
photo.image_remote_url = "http://example.com/cool-image.png"
photo.image.mime_type #=> "image/png"
photo.image.size #=> 43423
photo.image.original_filename #=> "cool-image.png"If you're using Shrine::Attacher directly, you can use
Attacher#assign_remote_url:
attacher.assign_remote_url("http://example.com/cool-image.png")
attacher.file.mime_type #=> "image/png"
attacher.file.size #=> 43423
attacher.file.original_filename #=> "cool-image.png"Downloader
By default, the file will be downloaded using Down.download from the Down
gem. This will use the Down::NetHttp backend by default, which is a wrapper
around open-uri.
You can pass options to the downloader via the :downloader option:
attacher.assign_remote_url url, downloader: {
headers: { "Authorization" => "Basic ..." },
read_timeout: 30, open_timeout: 30,
max_redirects: 5,
# ...
}You can also change the downloader:
# Gemfile
gem "http"require "down/http"
plugin :remote_url, downloader: -> (url, **options) {
Down::Http.download(url, **options) do |client|
client.follow(max_hops: 2).timeout(connect: 2, read: 2)
end
}Any Down::NotFound and Down::TooLarge exceptions will be rescued and
converted into validation errors. If you want to convert any other exceptions
into validation errors, you can raise them as
Shrine::Plugins::RemoteUrl::DownloadError:
plugin :remote_url, downloader: -> (url, **options) {
begin
RestClient.get(url)
rescue RestClient::ExceptionWithResponse => error
raise Shrine::Plugins::RemoteUrl::DownloadError, "remote file not found"
end
}Calling downloader
You can call the downloader directly with Shrine.remote_url:
# or YourUploader.remote_url(...)
file = Shrine.remote_url("https://example.com/image.jpg")
file #=> #<Tempfile:...>You can pass additional options as well:
# or YourUploader.remote_url(...)
Shrine.remote_url("https://example.com/image.jpg", headers: { "Cookie" => "..." })Uploader options
Any additional options passed to Attacher#assign_remote_url will be forwarded
to Attacher#assign (and Shrine#upload):
attacher.assign_remote_url(url, metadata: { "mime_type" => "text/plain" })Maximum size
It's a good practice to limit the maximum filesize of the remote file:
plugin :remote_url, max_size: 20*1024*1024 # 20 MBNow if a file that is bigger than 20MB is assigned, download will be terminated
as soon as it gets the "Content-Length" header, or the size of currently
downloaded content surpasses the maximum size. However, if for whatever reason
you don't want to limit the maximum file size, you can set :max_size to nil:
plugin :remote_url, max_size: nilErrors
If download errors, the error is rescued and a validation error is added equal to the error message. You can change the default error message:
plugin :remote_url, error_message: "download failed"
plugin :remote_url, error_message: -> (url, error) { I18n.t("errors.download_failed") }Background
If you want the file to be downloaded from the URL in the background, you can
use the shrine-url storage which allows you to assign a custom URL as cached
file ID, and pair that with the backgrounding plugin.
File extension
When attaching from a remote URL, the uploaded file location will inherit the
extension from the URL. However, some URLs might not have an extension. To
handle this case, you can use the infer_extension plugin to infer the
extension from the MIME type.
plugin :infer_extensionInstrumentation
If the instrumentation plugin has been loaded, the remote_url plugin adds
instrumentation around remote URL downloading.
# instrumentation plugin needs to be loaded *before* remote_url
plugin :instrumentation
plugin :remote_urlDownloading remote URLs will trigger a remote_url.shrine event with the
following payload:
| Key | Description |
|---|---|
:remote_url | The remote URL string |
:download_options | Any download options passed in |
:uploader | The uploader class that sent the event |
A default log subscriber is added as well which logs these events:
Remote URL (1550ms) – {:remote_url=>"https://example.com/image.jpg",:download_options=>{},:uploader=>Shrine}
You can also use your own log subscriber:
plugin :remote_url, log_subscriber: -> (event) {
Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, **event.payload)
}{"name":"remote_url","duration":5,"remote_url":"https://example.com/image.jpg","download_options":{},"uploader":"Shrine"}
Or disable logging altogether:
plugin :remote_url, log_subscriber: nil