6

I'm using jpegcam to allow a user to take a webcam photo to set as their profile photo. This library ends up posting the raw data to the sever which I get in my rails controller like so:

def ajax_photo_upload
  # Rails.logger.info request.raw_post
  @user = User.find(current_user.id)
  @user.picture = File.new(request.raw_post)

This does not work and paperclip/rails fails when you try to save request.raw_post.

Errno::ENOENT (No such file or directory - ????JFIF???

I've seen solutions that make a temporary file but I'd be curious to know if there is a way to get Paperclip to automatically save the request.raw_post w/o having to make a tempfile. Any elegant ideas or solutions out there?

UGLY SOLUTION (Requires a temp file)

class ApiV1::UsersController < ApiV1::APIController

  def create
    File.open(upload_path, 'w:ASCII-8BIT') do |f|
      f.write request.raw_post
    end
    current_user.photo = File.open(upload_path)
  end

 private

  def upload_path # is used in upload and create
    file_name = 'temp.jpg'
    File.join(::Rails.root.to_s, 'public', 'temp', file_name)
  end

end

This is ugly as it requires a temporary file to be saved on the server. Tips on how to make this happen w/o the temporary file needing to be saved? Can StringIO be used?

Thilo
  • 17,565
  • 5
  • 68
  • 84
AnApprentice
  • 108,152
  • 195
  • 629
  • 1,012
  • 2
    You do realize that using the FileSystem is a perfectly valid way of doing things right? In fact, the unix system is almost entirely FileSystem based, that's the whole point of why /dev/null exists. A much more valid concern I have about this solution is that it is susceptible to race condition. – Ken Li Oct 11 '12 at 02:06
  • Im sure it valid but create a file means I then need a cron job to clean delete all the temp files, which if that doesn't happen could result in filing up the HD and crashing the server. Why have one more thing to worry about? I'm very interested in elegantly solving this w/o need a temp file. Less moving parts? – AnApprentice Oct 11 '12 at 02:33
  • @AnApprentice, with the `Tempfile` class old files are cleaned up by the garbage collector. See my answer below. – Mischa Oct 11 '12 at 02:50

1 Answers1

14

The problem with my previous solution was that the temp file was already closed and therefore could not be used by Paperclip anymore. The solution below works for me. It's IMO the cleanest way and (as per documentation) ensures your tempfiles are deleted after use.

Add the following method to your User model:

def set_picture(data)
  temp_file = Tempfile.new(['temp', '.jpg'], :encoding => 'ascii-8bit')

  begin
    temp_file.write(data)
    self.picture = temp_file # assumes has_attached_file :picture
  ensure
    temp_file.close
    temp_file.unlink
  end
end

Controller:

current_user.set_picture(request.raw_post)
current_user.save

Don't forget to add require 'tempfile' at the top of your User model file.

Mischa
  • 42,876
  • 8
  • 99
  • 111
  • Thanks but the idea is to not use a temp file – AnApprentice Oct 11 '12 at 02:51
  • 1
    Then you have to hack Paperclip into doing what you want or use a different file processing library that doesn't require a file on the filesystem. (Carrierwave?) Anyway, this solution is valid if you are worried about having to clean up old files, because the garbage collector does it for you. What other concerns do you have with temp files? – Mischa Oct 11 '12 at 02:53
  • 1
    By the way, a normal file upload creates a temp file in a similar way as this code does. – Mischa Oct 11 '12 at 02:59
  • Thanks. Gave this a try and got the following error: "Encoding::UndefinedConversionError ("\xFF" from ASCII-8BIT to UTF-8):"... so I updated the line to: "Tempfile.open(['temp', '.jpg'], 'w:ASCII-8BIT') do |f|" which then resulted in the error: "Errno::ENOENT (No such file or directory - /Users/me/Sites/mysite/w:ASCII-8BIT/temp20121011-3095-1r5urg9.jpg.lock):" suggestions? Thanks – AnApprentice Oct 11 '12 at 21:07
  • Seems it should be: `Tempfile.open(['temp', '.jpg'], :encoding => 'ascii-8bit')` Does that work? – Mischa Oct 12 '12 at 01:15
  • That ends up erroring with "Paperclip::AdapterRegistry::NoHandlerError (No handler found for 14565):" – AnApprentice Oct 12 '12 at 01:45
  • 1
    Let me try to reproduce this. No clue from just the error. Just to make sure: your "ugly solution" worked? It produced a valid jpeg image that Paperclip could work with? – Mischa Oct 12 '12 at 05:26
  • Correct, my ugly solution does work. Here is the plugin: http://code.google.com/p/jpegcam/ -- thanks! – AnApprentice Oct 12 '12 at 05:29
  • 1
    The problem was that the tempfile was closed and could not be used with Paperclip anymore. I updated my answer. The solution above works for me. – Mischa Oct 12 '12 at 06:24
  • 1
    Fantastic, really elegant. Thank you Mischa – AnApprentice Oct 12 '12 at 17:58
  • I was having a problem with `self.picture` still being empty even after settings `self.picture = temp_file` ... I solved this by rewinding the temp_file back to the start `temp_file.rewind` , before trying to assign the file to the paperclip attribute – messinga May 04 '14 at 20:01