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
  • Possible duplicate of https://stackoverflow.com/questions/4600679/detect-mime-type-of-uploaded-file-in-ruby – Josh Brody Jan 17 '18 at 21:55
  • 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? –  Jan 17 '18 at 22:29
  • What happens if you do `FileMagic.new(FileMagic::MAGIC_MIME).file(@person.image.read)`? I'm not familiar with FileMagic but that _should_ work. – Josh Brody Jan 17 '18 at 22:42
  • When I try that I get the error, "cannot open `\211PNG\015\012\032\012' (No such file or directory)" –  Jan 17 '18 at 22:56
  • 1
    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`. – Halil Özgür Jan 20 '18 at 14:03
  • Doing that gives the error, "undefined method `path' for # Did you mean? pathmap" –  Jan 20 '18 at 17:49
  • Is your problem similar to this - https://stackoverflow.com/q/4893355/2096740 – arjun Jan 21 '18 at 20:06
  • @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. –  Jan 23 '18 at 17:37
  • @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? – arjun Jan 24 '18 at 13:21
  • @Natalia There is a simple JS solution, https://stackoverflow.com/a/29672957/2096740. If this is what you are looking for good, other wise tell me. – arjun Jan 24 '18 at 13:24
  • 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? –  Jan 24 '18 at 16:17
  • @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 - https://www.owasp.org/index.php/Unrestricted_File_Upload – arjun Jan 24 '18 at 18:44
  • 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. –  Jan 24 '18 at 19:43

2 Answers2

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
goose3228
  • 361
  • 1
  • 8
3

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

Dipil
  • 2,628
  • 1
  • 20
  • 25
  • Hi, I tried this line but I'm getting the error, "undefined method `read' for #" –  Jan 23 '18 at 17:35
  • @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. – Dipil Jan 24 '18 at 06:17
  • 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. –  Jan 24 '18 at 14:57
  • What library/gem are you using to upload and store the png file in Person? – Dipil Jan 25 '18 at 10:39
  • I included "gem 'rmagick'" in my Gemfile. I'm open to others. –  Jan 25 '18 at 14:52