How To Store Thumbnails and Full Size Images In Different Directories With attachment_fu
Catchy title!
When you want to upload and resize images in Rails, attachment_fu is your best bet. Mike Clark wrote a good tutorial explaining how to get started.
If you are storing the images on the file system, attachment_fu puts any given full size image and its thumbnails in the same directory (with different filenames). For example:
photos/0000/0001/piccie.jpg
photos/0000/0001/piccie_small.jpg
You can change the path and customise the filename by overriding the full_filename method in your model. The original implementation is:
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s
File.join(RAILS_ROOT, file_system_path, *partitioned_path(thumbnail_name_for(thumbnail)))
end
This is how it works:
The file_system_path is set to your model’s (or your thumbnail model’s) path_prefix. By default this is the model’s table name under public. So if your model is Photo, file_system_path becomes public/photos.
The following line is more involved.
thumbnail_name_for(thumbnail) returns the image’s filename. If thumbnail is nil, the original filename is returned — e.g. piccie.jpg. If a value is given, such as small, it returns the thumbnail’s filename — e.g. piccie_small.jpg.
The partitioned_path method returns a path based on the image’s database ids such that your file system won’t be brought to its knees by overflowing directories.
The File.join line concatenates these components to give a path like /path/to/your/app/public/photos/0000/0001/piccie.jpg.
So far, so good. You can easily change the paths to store your images wherever you like, bearing in mind that a full size image and its thumbnails will be stored in the same directory.
But what happens if you want to store full size images and their thumbnails in different directories? It’s not quite as easy as it first seems.
You might think you could branch on the thumbnail argument like this:
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s
if thumbnail
File.join(RAILS_ROOT, file_system_path, 'thumbnails', *partitioned_path(thumbnail_name_for(thumbnail)))
else
File.join(RAILS_ROOT, file_system_path, 'fullsizes', *partitioned_path(thumbnail_name_for(thumbnail)))
end
end
But it turns out that the other parts of attachment_fu don’t pass in thumbnail when you might expect and all the images end up in the fullsizes directory path.
This, however, does the trick:
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s
if thumbnail.blank? && self.thumbnail.blank?
File.join(RAILS_ROOT, file_system_path, 'fullsize', *partitioned_path(thumbnail_name_for(thumbnail)))
else
File.join(RAILS_ROOT, file_system_path, 'thumb', *partitioned_path(thumbnail_name_for(thumbnail)))
end
end
The point to notice is that thumbnail is an argument to the method while self.thumbnail is an attribute of your model. They are not the same.
It reminds me of that baffling joke told by ontologists: “What’s the difference between a duck? One of its legs are both the same.” Except the other way around.
Posted in Rails

5 Comments
Jump to comment form