Skip to main content

Determine MIME Type

The determine_mime_type plugin allows you to determine and store the actual MIME type of the file analyzed from file content.

plugin :determine_mime_type

By default the UNIX file utility is used to determine the MIME type, and the result is automatically written to the mime_type metadata field. You can choose a different built-in MIME type analyzer, for example:

plugin :determine_mime_type, analyzer: :marcel

Analyzers

The following analyzers are accepted:

NameDescription
:file(Default). Uses the file utility to determine the MIME type from file contents. It is installed by default on most operating systems, but the Windows equivalent needs to be installed separately.
:fastimageUses the fastimage gem to determine the MIME type from file contents. Fastimage is optimized for speed over accuracy. Best used for image content.
:filemagicUses the ruby-filemagic gem to determine the MIME type from file contents, using a similar MIME database as the file utility. Unlike the file utility, ruby-filemagic works on Windows without any setup.
:mimemagicUses the mimemagic gem to determine the MIME type from file contents. Unlike ruby-filemagic, mimemagic is a pure-ruby solution, so it will work across all Ruby implementations.
:marcelUses the marcel gem to determine the MIME type from file contents. Marcel is Basecamp's wrapper around mimemagic, it adds priority logic (preferring magic over name when given both), some extra type definitions, and common type subclasses (including Keynote, Pages, etc).
:mime_typesUses the mime-types gem to determine the MIME type from the file extension. Note that unlike other solutions, this analyzer is not guaranteed to return the actual MIME type of the file.
:mini_mimeUses the mini_mime gem to determine the MIME type from the file extension. Note that unlike other solutions, this analyzer is not guaranteed to return the actual MIME type of the file.
:content_typeRetrieves the value of the #content_type attribute of the IO object. Note that this value normally comes from the "Content-Type" request header, so it's not guaranteed to hold the actual MIME type of the file.

You'll need to ensure the file utility or gem is in installed on your system for Shrine to use the analyzer.

Analyzer Options

Currently, Marcel is the only analyzer that accepts options to be passed in. Analyzer options can be set in one of two ways:

plugin :determine_mime_type, analyzer: :marcel, analyzer_options: { filename_fallback: true }

# or

plugin :determine_mime_type, analyzer: -> (io, analyzers) do
  mime_type = analyzers[:marcel].call(io, filename_fallback: true)
end

The :filename_fallback option for Marcel analyzer lets it use the filename as a fallback option when it fails to determine the MIME type from the file content. Set :filename_fallback to true in order to fallback to using the filename or set to false to not use the filename (this is the default).

Issues

If you find using a single analyzer is not able to recognize all of the file types in your application, you can build your own custom analyzer and combine the built-in analyzers. For example, if you want to correctly determine MIME type of .css, .js, .json, .csv, .xml, or similar text-based files, you can combine file and mime_types analyzers:

plugin :determine_mime_type, analyzer: -> (io, analyzers) do
  mime_type = analyzers[:file].call(io)
  mime_type = analyzers[:mime_types].call(io) if mime_type == "text/plain"
  mime_type
end

API

You can also use the methods for determining the MIME type directly:

# or YourUploader.determine_mime_type(io)
Shrine.determine_mime_type(io) #=> "image/jpeg" (calls the defined analyzer)
# or just
Shrine.mime_type(io) #=> "image/jpeg" (calls the defined analyzer)

# or YourUploader.mime_type_analyzers
Shrine.mime_type_analyzers[:file].call(io) #=> "image/jpeg" (calls a built-in analyzer)

Instrumentation

If the instrumentation plugin has been loaded, the determine_mime_type plugin adds instrumentation around MIME type analyzation.

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

Analyzing MIME type will trigger a mime_type.shrine event with the following payload:

KeyDescription
:ioThe IO object
:uploaderThe uploader class that sent the event

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

MIME Type (33ms) – {:io=>StringIO, :uploader=>Shrine}

You can also use your own log subscriber:

plugin :determine_mime_type, log_subscriber: -> (event) {
  Shrine.logger.info JSON.generate(name: event.name, duration: event.duration, **event.payload)
}
{"name":"mime_type","duration":24,"io":"#<StringIO:0x00007fb7c5b08b80>","uploader":"Shrine"}

Or disable logging altogether:

plugin :determine_mime_type, log_subscriber: nil