How to Handle Multiple File Types?

Hi,

I have a model Document, with an attachment attribute file_data, that can have an attached file of multiple types (image, video, pdf, etc.). I’m also attaching multiple files to a single resource (a folder having many documents).

I’ve read that the recommendation is to use one uploader class per file type, because that design pattern allows you to handle attachment logic in a flexible manner. However, I am confused on how to assign which uploader to use when the user uploads a file (since the file could be an image, video, or some other type).

Does Shrine determine the MIME type of the file (with the determine_mime_type plugin) before the uploader is called? If so, then could it suffice to have a set of conditional statements that specifies which uploader to use?

For example:

class Document < ApplicationRecord
  belongs_to :folder
  
  # PSEUDOCODE
  case file.mime_type
  when image
    include ImageUploader::Attachment(:file)
  when video 
    include VideoUploader::Attachment(:file)
  when pdf
    include PDFUploader::Attachment(:file)
  end
end

Also, is it fine for different uploaders to add the same virtual attribute to my model?

Here’s my current code structure:

require "shrine"
require "shrine/storage/s3"

Shrine.storages = { 
  cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
  store: Shrine::Storage::S3.new(**s3_options),
}

Shrine.plugin :activerecord
Shrine.plugin :instrumentation 
Shrine.plugin :determine_mime_type, analyzer: :marcel, log_subscriber: nil
Shrine.plugin :cached_attachment_data 
Shrine.plugin :restore_cached_data  

class BaseUploader < Shrine
  # plugins and uploading logic
end

class ImageUploader < BaseUploader
  # plugins and uploading logic
end

class VideoUploader < BaseUploader
  # plugins and uploading logic
end

class PDFUploader < BaseUploader
  # plugins and uploading logic
end

class Document < ApplicationRecord
  belongs_to :folder
  
  # Handle image attachment logic
  include ImageUploader::Attachment(:file)
end

Hi Andrew,

Note that the documentation just says it’s common to have an uploader per file type, but it encourages you to make your own choice based on your requirements. In your case, it sounds like you’re best off just have a single uploader that handles all types of files. Shrine should enable you to easily branch off your logic for specific types when you need to.

For example, if you’re generating derivatives, you can conditionally perform processing depending on the type of the attached file:

Attacher.derivatives do |original|
  case file.mime_type
  when /^image\//
    # ... image processing ...
  when /^video\//
    # ... video processing ...
  when ...
    # ...
  end
end

Does Shrine determine the MIME type of the file (with the determine_mime_type plugin) before the uploader is called?

No, the attachment module is inclusion is happening immediately when your model is being loaded, at which point there is no file, because files are uploaded at the time of a request and are available at instance level of your model.

Also, is it fine for different uploaders to add the same virtual attribute to my model?

If you include multiple attachment modules to a model for the same attribute, the last one will win.

1 Like