Skip to main content

Data URI

The data_uri plugin enables you to upload files as data URIs. This plugin is useful for example when using HTML5 Canvas.

plugin :data_uri

Usage​

The plugin will add the #<name>_data_uri writer to your model, which parses the given data URI string and uploads it to temporary storage:

photo.image_data_uri = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA"
photo.image.mime_type #=> "image/png"
photo.image.size      #=> 43423

If you're using Shrine::Attacher directly, you can use Attacher#assign_data_uri:

attacher.assign_data_uri("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA")
attacher.file.mime_type #=> "image/png"
attacher.file.size      #=> 43423

Errors​

If the data URI wasn't correctly parsed, an error message will be added to the attachment column. You can change the default error message:

plugin :data_uri, error_message: "data URI was invalid"
plugin :data_uri, error_message: ->(uri) { I18n.t("errors.data_uri_invalid") }
plugin :data_uri, error_message: ->(uri, error) { I18n.t("errors.data_uri.#{error.message}") }

Maximum size​

It's a good practice to limit the maximum size of the data URI content:

plugin :data_uri, max_size: 10*1024*1024 # 10 MB

Now if content bigger than 10MB is assigned, parsing will fail the same way as for an invalid data URI. The size is calculated from the data URI before the content is decoded, so oversized content never gets loaded into memory. It's exact for base64 content, while for percent-encoded content the encoded length is used, which can overestimate.

Uploader options​

Any options passed to Attacher#assign_data_uri will be forwarded to Attacher#assign (and Shrine#upload):

attacher.assign_data_uri(uri, metadata: { "filename" => "nature.jpg" })

File extension​

A data URI doesn't convey any information about the file extension, so when attaching from a data URI, the uploaded file location will be missing an extension. If you want the upload location to always have an extension, you can load the infer_extension plugin to infer it from the MIME type.

plugin :infer_extension

Parsing data URI​

If you just want to parse the data URI and create an IO object from it, you can do that with Shrine.data_uri. If the data URI cannot be parsed, a Shrine::Plugins::DataUri::ParseError will be raised.

# or YourUploader.data_uri(...)
io = Shrine.data_uri("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA")
io.content_type #=> "image/png"
io.size         #=> 21
io.read         # decoded content

When the content type is ommited, text/plain is assumed. The parser also supports raw data URIs which aren't base64-encoded.

# or YourUploader.data_uri("...")
io = Shrine.data_uri("data:,raw%20content")
io.content_type #=> "text/plain"
io.size         #=> 11
io.read         #=> "raw content"

You can also assign a filename:

io = Shrine.data_uri("data:,content", filename: "foo.txt")
io.original_filename #=> "foo.txt"

Generating data URI​

This plugin also adds UploadedFile#data_uri method, which returns a base64-encoded data URI of the file content, and UploadedFile#base64, which simply returns the file content base64-encoded.

uploaded_file.data_uri #=> "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA"
uploaded_file.base64   #=> "iVBORw0KGgoAAAANSUhEUgAAAAUA"

Instrumentation​

If the instrumentation plugin has been loaded, the data_uri plugin adds instrumentation around data URI parsing.

# instrumentation plugin needs to be loaded *before* data_uri
plugin :instrumentation
plugin :data_uri

Parsing data URIs will trigger a data_uri.shrine event with the following payload:

KeyDescription
:data_uriThe data URI string
:uploaderThe uploader class that sent the event

A default log subscriber is added as well which logs these events:

Data URI (5ms) – {:uploader=>Shrine}

You can also use your own log subscriber:

plugin :data_uri, log_subscriber: -> (event) {
  Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, uploader: event[:uploader])
}
{"name":"data_uri","duration":5,"uploader":"Shrine"}

Or disable logging altogether:

plugin :data_uri, log_subscriber: nil