Saving Mime Types with Dragonfly
Eli Fatsi, Former Development Director
Article Categories:
Posted on
Cacheing mime types for Dragonfly attachments can save your server from a lot work. Keep reading to find out how!
We often reach for the Dragonfly gem when we need to provide photo and file upload access to an application's user base, and display those uploads in various sizes and shapes. On a recent project, users were allowed to upload either a photo or a PDF for a given attachment, which was all well and fine until we set out to display a thumbnail of the attachment.
The Problem #
There was the challenge of rendering PDF thumbnails (see ImagePdfHelper gist if curious), but also the challenge of knowing what kind of attachment we were dealing with. You can always call #mime_type
on a Dragonfly attachment instance, however if you have your files stored remotely (eg: S3), then doing so requires a full download of the attachment in order for image processing to take place on your server. This additional download should be avoided if possible, so what does it look like to cache an upload's mime type at upload time?
The Solution #
class Foo < ApplicationRecord
dragonfly_accessor :file do
after_assign do
self.assign_attributes("file_mime_type" => attachment.mime_type)
end
after_unassign do
self.assign_attributes("file_mime_type" => nil)
end
end
end
Nothing too fancy going on here. We simply tap into Dragonfly's supplied after_assign
and after_unassign
hooks in order to cache an attachment's mime type on upload, and clear it on removal. This tactic requires the addition of a file_mime_type
field to the model's database table to store the mime type string in the database on the parent model.
The Comprehensive Solution #
We needed this behavior in a handful of models however, so I pulled the logic into a module which defined a custom override of the dragonfly_accessor
method, and included the module in ApplicationRecord
, effectively replacing the default implementation:
class ApplicationRecord
include CaptureMimeType
end
class Foo < ApplicationRecord
dragonfly_accessor :file, mime_type: true
end
module CaptureMimeType
def self.included(base)
base.instance_eval do
def dragonfly_accessor(attribute, opts={}, &config_block)
if opts.delete(:mime_type)
# If the :mime_type option is passed in, cache the
# attachment's mime_type after assignment
super(attribute, opts) do
config_block.call if config_block
after_assign do |attachment|
self.assign_attributes("#{attribute}_mime_type" => attachment.mime_type)
end
after_unassign do |attachment|
self.assign_attributes("#{attribute}_mime_type" => nil)
end
end
else
# Otherwise, proceed with normal dragonfly setup
super
end
end
end
end
end
This override recognizes the new :mime_type
option, and declares the necessary hooks for caching and clearing the mime type. The rest of the method definition exists for backwards compatibility. If the mime_type
option is not supplied, then a vanilla call to super
will defer to Dragonfly's default behavior entirely. However if the option is supplied, a call to super
is still utilized to trigger the default behavior and respect other passed in parameters, but with a custom block that defines the assignment hooks.
It's worth noting that this implementation has the potential break down should the method signature for dragonfly_accessor
change in a future release, but for now, this works swimmingly.