attachment_fu Now With Local File Fu

Posted by Ben Reubenstein Fri, 04 Jan 2008 17:50:00 GMT

In the beginning there was file_column. It was an excellent plugin for handling file uploads and image processing with the added bonus of being able to simply pass a file to it and have it work without a file upload via a form. One thing that file_column didn't do was fill in your db with file attribute goodness that could be used to create logic around a particular file. attachment_fu handled this along with the ability to use multiple image processors. For detailed info on attachment_fu, check out Mike Clark's article.

In order to add some local_file_fu to attachment_fu so you can pass a local file directly to it, you have to take your local file and turn it into a temporary file that you can pass to attachment_fu's uploaded_data method. I altered the solution outlined here for my solution.

1. Create a class in your models directory in a file called local_file.rb.

require 'tempfile'
class LocalFile
 # The filename, *not* including the path, of the "uploaded" file
 attr_reader :original_filename
 # The content type of the "uploaded" file
 attr_reader :content_type

 def initialize(path)
  raise "#{path} file does not exist" unless File.exist?(path)
  content_type ||= @@image_mime_types[File.extname(path)]
  raise "Unrecognized MIME type for #{path}" unless content_type
  @content_type = content_type
  @original_filename = File.basename(path)
  @tempfile = Tempfile.new(@original_filename)
  FileUtils.copy_file(path, @tempfile.path)
 end

 def path #:nodoc:
  @tempfile.path
 end
 alias local_path path

 def method_missing(method_name, *args, &block) #:nodoc:
  @tempfile.send(method_name, *args, &block)
 end
end


2. In order for attachment_fu to pass validations, you need to set the mime type of the file. This would usually come from the form when it is uploaded, but since we are using a local file, we'll set our mime types in environment.rb. At the end of the file add the various mime types you will need:

@@image_mime_types ||= { ".gif" => "image/gif", ".ief" => "image/ief", ".jpe" => "image/jpeg", ".jpeg" => "image/jpeg", ".jpg" => "image/jpeg", ".pbm" => "image/x-portable-bitmap", ".pgm" => "image/x-portable-graymap", ".png" => "image/png", ".pnm" => "image/x-portable-anymap", ".ppm" => "image/x-portable-pixmap", ".ras" => "image/cmu-raster", ".rgb" => "image/x-rgb", ".tif" => "image/tiff", ".tiff" => "image/tiff", ".xbm" => "image/x-xbitmap", ".xpm" => "image/x-xpixmap", ".xwd" => "image/x-xwindowdump" }.freeze

3. Now in your code that creates the model that has_attachments you can simply do the following:

model = Model.new()
model.uploaded_data = LocalFile.new(FULL_PATH_TO_FILE)
model.save


As always, comment on anything you have issues with or suggestions.

Posted in  | Tags ,  | 8 comments

Comments

  1. Micah Calabrese said about 1 month later:

    This is great, thanks! One note, I had to downcase the file extensions to account for extensions in caps:

    contenttype ||= @@imagemime_types[File.extname(path).downcase]

  2. Andy Stewart said 4 months later:

    You could also use the MIME::Types gem, so your code doesn't have to become expert on MIME types.

    There's an example in this post by, ahem, me, in the section Missing MIME Type.

  3. Nick said 5 months later:

    Great one! I've spent ages trying to work this out.

  4. Jorge said 6 months later:

    I'm using Paperclip and it has the same issue. I did what you guys did above and added a "to_tempfile" method that returns the tempfile as paperclip needs this in its Attachment.assign method. Everything works out fine except that it corrupts the original file. Any thoughts?

  5. Ben Reubenstein said 6 months later:

    @jorge You might have better luck trying the paperclip google group. Would have to see some code to give some thoughts ;).

  6. Fred Z said 7 months later:

    I was able to simplify the class a bit, and combine the mime types. Here it is:

    class Admin::LocalAttachment < File

    require 'mime/types'

    attrreader :originalfilename attrreader :contenttype

    def initialize( path ) super( path ) raise "#{path}: file does not exist" unless File.exist?( path ) @original_filename = File.basename( path ) mime = MIME::Types.typefor( @originalfilename )[0] raise "Unrecognized MIME type for #{path}" if mime.blank? @contenttype = mime.contenttype end

    alias local_path path

    def size #:nodoc File.size( self.path ) end

    end

  7. Foton said 7 months later:

    Do you have any idea how add a attachment from DB. I have incoming attachments stored in MySql table (filename, content [blob],...) and I am processing them to be stored on filesystem. With plugin 'acts_as_attachement' I just use:

    Priloha.new({ :filename => p_incoming.filename, :attachmentdata => pincoming.content})

    but with plugin 'attachmentfu' this way no longer works. Using :uploadeddata needs something more, then just raw file data.

    Priloha.new({ :filename => p_incoming.filename, :ulploadeddata => pincoming.content})

  8. Foton said 7 months later:

    Finaly I solved it. :

    Priloha.new({ :filename => p_incoming.filename, :tempdata => pincoming.content, :size => p_incoming.content.size, :contenttype => p.incoming.contenttype })

   Comment Markup Help Preview comment