Mime type nil due to marcel detecting empty file when it is not?

Don’t know much about IO, but it seems like using io.eof? to check for empty file is causing the mime type determination to fail prematurely and return nil for a valid file.

From: /usr/local/bundle/gems/shrine-3.2.1/lib/shrine/plugins/determine_mime_type.rb @ line 151 Shrine::Plugins::DetermineMimeType::MimeTypeAnalyzer#extract_with_marcel:

    147: def extract_with_marcel(io, options)
    148:   require "marcel"
    150:   binding.pry
 => 151:   return nil if io.eof? # marcel returns "application/octet-stream" for empty files
    153:   filename = (options[:filename_fallback] ? extract_filename(io) : nil)
    154:   Marcel::MimeType.for(io, name: filename)
    155: end

[1] pry(#<Shrine::Plugins::DetermineMimeType::MimeTypeAnalyzer>)> io.eof?
=> true
[2] pry(#<Shrine::Plugins::DetermineMimeType::MimeTypeAnalyzer>)> io.size
=> 1964
[3] pry(#<Shrine::Plugins::DetermineMimeType::MimeTypeAnalyzer>)> Marcel::MimeType.for(io)
=> "image/svg+xml"
[4] pry(#<Shrine::Plugins::DetermineMimeType::MimeTypeAnalyzer>)> io.rewind
=> 0
[5] pry(#<Shrine::Plugins::DetermineMimeType::MimeTypeAnalyzer>)> io.eof?
=> false
[6] pry(#<Shrine::Plugins::DetermineMimeType::MimeTypeAnalyzer>)> io.size
=> 1964

Calling #eof? was a way for Shrine to check whether the file was empty without calling #size (in order to support IO objects that don’t know their size).

I would say that the root issue here is that for some reason the file was not rewinded before determining its MIME type. This may have happened on the application side, because Shrine is normally dilligent about rewinding the file.

Thanks Janko for clarifying how #eof is used.

It’s been a while and my uploaders have been refactored a lot, and I reordered the plugins quite a few times.

I tried reenabling marcel again today, and now it is passing for most of the tests, except when the document is a JSON. I realized this is possibly due to an underlying bug on marcel itself -

I’ll use the filename fallback approach for this one. Will stick with marcel for now.

Thanks for your help!

Also, I figured out why the file was already at #eof. There’s a custom validator that opens up the file prior to it being passed to shrine. So this is definitely not a bug.

However, I’m thinking would it make sense for shrine to just rewind the file as a precaution before attempting to determine the mime type? (not sure how that affects performance / other IO objects though).