How do I figure out the mime type without writing a file?
Asked Answered
A

2

6

This is with Rails 5 and ruby-filemagic. Currently I'm saving an uploaded image to my database in addition to its mime type. I have this code in my controller

  def create
    @person = Person.new(person_params)
    if @person.image
      cur_time_in_ms = DateTime.now.strftime('%Q')
      file_location = "/tmp/file#{cur_time_in_ms}.ext"
      File.binwrite(file_location, @person.image.read)
      fm = FileMagic.new(FileMagic::MAGIC_MIME)
      @person.content_type = fm.file(file_location, true)
    end
    if @person.save
      redirect_to @person
    else
      # This line overrides the default rendering behavior, which
      # would have been to render the "create" view.
      render "new"
    end
  end

The thing that bothers me with this approach is taht I have to write a file to the file system before figuring out its mime type. That seems wasteful. How can I figure out the mime type without creating a file first?

Edit: In response to the answer given, I rearranged things, but now the "content_type" becomes "application/x-empty" even when I upload a valid png file. Here's the code

if @person.image

  cur_time_in_ms = DateTime.now.strftime('%Q')
  file_location = "/tmp/file#{cur_time_in_ms}.ext"
  File.binwrite(file_location, @person.image.read)
  file = File.open(file_location, "rb")
  contents = file.read
  # Scale image appropriately
  #img = Magick::Image::read(file_location).first
  @person.content_type = FileMagic.new(FileMagic::MAGIC_MIME).buffer(@person.image.read, true)
  @person.image = contents
Antetype answered 17/1, 2018 at 21:52 Comment(13)
Possible duplicate of #4601179Letha
Are you referring to the accepted answer? In it they are still referencing a file -- "FileMagic.new(FileMagic::MAGIC_MIME).file(FILE)" as opposed to just figuring out the file type from binary data. Am I mistaken?Antetype
What happens if you do FileMagic.new(FileMagic::MAGIC_MIME).file(@person.image.read)? I'm not familiar with FileMagic but that should work.Letha
When I try that I get the error, "cannot open `\211PNG\015\012\032\012' (No such file or directory)"Antetype
Depending on the libraries you use, I think @person.image is already a temporary file. Could you try fm.file(@person.image.path, true)? For a true IO data you'd want fm.buffer.Grizzled
Doing that gives the error, "undefined method `path' for #<String:0x007f8a5301fba0> Did you mean? pathmap"Antetype
Is your problem similar to this - https://mcmap.net/q/1748571/-how-do-i-get-a-temporary-file-object-of-correct-content-type-without-writing-to-disk-directly-from-a-zipentry-rubyzip-paperclip-rails-3/2096740Tullusus
@arjun, thx for the link although the problem there looks a little different. Looks like they're dealing with zip files. My image is just uploaded as is.Antetype
@Natalia So what you are basically making an attempt is to find what type of image the user has uploaded(png, jpg, gif) before saving it to your servers?Tullusus
@Natalia There is a simple JS solution, https://mcmap.net/q/107424/-how-to-check-file-mime-type-with-javascript-before-upload. If this is what you are looking for good, other wise tell me.Tullusus
Hi @arjun, I checked out that link you sent and it does seem to be exacctly what I want -- extracting the mime type from teh uploaded file. My quesiton is, can the JS logic be fooled? That is, if I uploaded a PNG file with a ".jpg" extension, will it still tell me its a "image/png" mime type?Antetype
@Natalia They do address it in the answer. Even the gem which you are using does not depend on the extension to determine the mime-type. What Josh Broody said, is the way to do it server-side. I would be more concerned about these in a production environment - owasp.org/index.php/Unrestricted_File_UploadTullusus
Your JS solution is the only thing I've got working so far for determining the mine type. The lnk you sent about the unrestricted uploads raises good issues, but should I ask about that in another question? Unless it pertains to mime-types in some way.Antetype
B
6

Assuming you are uploading file via html form, IO object should already have mime type, you can get it like that:

mime = params[:file].content_type
Blague answered 26/1, 2018 at 5:57 Comment(1)
It was actually @person.image.content_type but close enough.Antetype
M
3

Try using the buffer method instead of file i.e. FileMagic.new(FileMagic::MAGIC_MIME).buffer(@person.image.read, true)

Megargee answered 22/1, 2018 at 12:15 Comment(5)
Hi, I tried this line but I'm getting the error, "undefined method `read' for #<String:0x007f89d3889eb0>"Antetype
@Natalia was this part of your code File.binwrite(file_location, @person.image.read) working before? Because read seems to be used only there. If it was working before, please put a more extended stacktrace so it can be debugged.Megargee
I edited my ansewr to incude how I'm doing things now. I rearranged the code so I don't get an error any more, but the content type is coming out to be "application/x-empty" even when I upload a PNG file, so I think I'm still not doing something right.Antetype
What library/gem are you using to upload and store the png file in Person?Megargee
I included "gem 'rmagick'" in my Gemfile. I'm open to others.Antetype

© 2022 - 2024 — McMap. All rights reserved.