Extending acts_as_attachment
I have been using Rick Olson’s excellent acts_as_attachment Rails plugin.
The plugin offers two alternatives for attachment storage: on the local filesystem or in the database. When using the database, images are placed into the hierarchy:
/public/#{MODEL.table_name}/#{INSTANCE.id}/FILENAME
The acts_as_attachment DVD cover tutorial will produce filenames like:
/public/dvd_covers/23/batman.jpg
The on-disk storage for all images is at the same level of hierarchy. Some filesystems will exhibit in slow access and update operations as the number of items increases.
In my model, I am overloading full_filename to avoid any FS performance problems with huge numbers of directories. I am producing hierarchy of the form:
/public/#{MODEL.table_name}/XX/YY/#{INSTANCE.id}/FILENAME
The XX and YY pieces have at most 256 elements at any one directory level. This two-level strategy remains well-sized until about 16M attachments are saved. Additional levels could be added, depending on the application.
The acts_as_attachment infrastructure allows relocating the on-disk storage through its :file_system_path parameter, but it still produces a flat directory structure. We can get a more balanced directory hierarchy with the following overriding of the full_filename method.
class DvdCover < ActiveRecord::Base
acts_as_attachment :storage => :file_system # + other options?
@@HierarchyLevels = 2
def full_filename(thumbnail = nil)
file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:file_system_path]
parts = [RAILS_ROOT, file_system_path] +
(1..@@HierarchyLevels).map { |i| partial_path(i) } +
[ (parent_id || id).to_s, thumbnail_name_for(thumbnail)]
File.join(parts)
end
protected
# Given i, extract the ith 8-bit hex representation from our primary id.
def partial_path(i)
“%02X” % ((parent_id || id) >> (8*i)) % 256
end
end
The HierarchyLevels defines how many levels of ID-produced hierarchy is generated. A two-level scheme can handle 16 million objects before it begins to become unbalanced. You can move to a three-level (XX/YY/ZZ) scheme by setting HierarchyLevels to 3.
You may notice that the example above still creates a directory for each database ID. This ID directory only contains a few files; it isn’t really necessary. Further, the user’s original filename is preserved into our on-disk (and database) storage. Our users might not want these filenames published.
I will discuss how to remove the ID directory and eliminate user-specified filenames in a follow-on post.
4 Comments so far
Leave a reply


[…] As mentioned in my previous post about acts_as_attachment, I am also altering the default directory structure. Putting it all together, here is my attachment model if we were working with the DVD Cover tutorial. […]
[…] If you are using acts_as_attachment (or attachment_fu) to upload the files then you can use this strategy to create more directories. http://blog.spotstory.com/2006/11/10/extending-acts_as_attachment/ […]
[…] On 10/3/07, Bakki Kudva <bakki.kudva@…> wrote: > > If you are using acts_as_attachment (or attachment_fu) to upload the > files then you can use this strategy to create more directories. > http://blog.spotstory.com/2006/11/10/extending-acts_as_attachment/ > > If not, I would recommend using attachment_fu to do uploads. […]
[…] If you are using acts_as_attachment (or attachment_fu) to upload the files then you can use this strategy to create more directories. http://blog.spotstory.com/2006/11/10/extending-acts_as_attachment/ […]