如果定制attachment_fu上传文件的路径和文件名

原文出处:http://the.railsi.st/2007/8/21/how-to-customize-attachment_fu-file-names

For uploading files, it's pretty hard to beat attachment_fu. But it can be overkill for smaller projects.

One issue is that attachment_fu uses id partioning. This is a great way to overcome native file system limitations when you have more than 32,000 attachments. By segmenting files into different directories, you can have millions of attachments, if necessary. Empahsis on "if necessary". It usually isn't.

Also, attachment_fu preserves original filenames. While this make sense for many projects, sometimes you need to have control over the naming of attachments.

Since a lot of people use Mike Clark's excellent File Upload Fu tutorial, let's use that as our starting point for customizing file names.

If we complete the tutorial, here's how attachment_fu will store our first image upload:

public/mugshots/0000/0001/chunkybacon.png 
public/mugshots/0000/0001/chunkybacon_thumb.png

Hmmm, not bad. But I'd like to customize things:

  • Images should be stored in public/images/
  • Thumbnails should be organized by size
  • ID partioning (0000/0001/) should be disabled
  • Images should be renamed with the Mugshot id

So let's open up our Mugshot model and tweak it a bit.

class Mugshot < ActiveRecord::Base 
  has_attachment :content_type => :image,
                 :storage => :file_system,
                 :max_size => 500.kilobytes,
                 :resize_to => '320x200>',
                 :thumbnails => { :thumb => '100x100>' },
                 :path_prefix => 'public/images/mugshots'
  validates_as_attachment


  def full_filename(thumbnail = nil)
    file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix]
    case self.thumbnail
    when "thumb"
      File.join(RAILS_ROOT, file_system_path, 'thumb', thumbnail_name_for(thumbnail, self.parent_id))
    else
      File.join(RAILS_ROOT, file_system_path, 'fullsize', thumbnail_name_for(thumbnail, self.id))
    end
  end


  def thumbnail_name_for(thumbnail = nil, asset = nil)
    extension = filename.scan(/\.\w+$/)
    return "#{asset}#{extension}"
  end
end

Now, when we upload an image, it will be stored like so:

public/images/mugshots/fullsize/2.png 
public/images/mugshots/thumb/2.png

How does this work?

Well, first, we customize the :path_prefix value in has_attachment to set the base location of our files.

Second, we override the full_filename method to force attachment_fu to save each thumbnail type into its own directory. This way, all large thumbnails are stored in images/mugshots/fullsize and all small thumbnails are stored in images/mugshots/thumb. (By default, attachment_fu stores all thumbnail sizes for an object in a single directory.)

Lastly, we override the thumbnail_name_for method to customize the filename to our liking... in this case, the file name will consist of the parent mugshot id, plus the original file's file extension.

That's all we need to do... now our files are stored exactly where we want them!

(Thanks to AirBlade Software for showing the way.)

Extra credit

If your attachments belong to another model (like a user), you'll need to use a before filter to associate the parent model id with the thumbnail you wish to store.

before_thumbnail_saved do |record, thumbnail| 
  thumbnail.user_id = record.user_id
end

You'll also need an extra migration to store the parent id. (Don't use the parent_id field... that's reserved by attachment_fu.)

Extra, extra credit

What if you want three sizes? Large (original image), medium, and small?

has_attachment :content_type => :image, 
               :storage => :file_system,
               :max_size => 500.kilobytes,
               :thumbnails => { :small => '100x100>', :medium => '200x200>' },
               :path_prefix => 'public/images/mugshots'
validates_as_attachment


def full_filename(thumbnail = nil)
  file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix]
  case self.thumbnail
  when "small"
    File.join(RAILS_ROOT, file_system_path, 'small', thumbnail_name_for(thumbnail, self.parent_id))
  when "medium"
    File.join(RAILS_ROOT, file_system_path, 'medium', thumbnail_name_for(thumbnail, self.parent_id))
  else
    File.join(RAILS_ROOT, file_system_path, 'large', thumbnail_name_for(thumbnail, self.id))
  end
end

你可能感兴趣的:(UP,ActiveRecord,Rails)