Copying Files Between Models With file_column
Posted by Craig Ambrose on November 28, 2006 at 10:12 PM
I’m using the file_column plugin a lot. It lets you treat a file as an attribute on one of your models, and all you need to do to store it is provide a string column in your database table.
A lot of people have expressed concern that file_column doesn’t seem to have been worked on by it’s author at all this year. This maybe so, but don’t let that deter you from the fact that it is still currently the simplest and best way to handle images in rails applications.
One of the first snags that I’ve hit with file_column so far took me a few hours to nut out today, so I’d like to share it with you, gentle reader, in the hope that it might save you the same effort. I had a model object, which contained a file_column (and thus, potentially, a file), and I wanted to clone it. As a result of this, I wanted the new object to have it’s own separate copy of the file.
If you just naively copy the file_column (in my case, called “image”, on a model called “gift”), then file_column warns you that you’re just giving it a string, and thus you probably forgot a multipart declaration on your form. As you can see, file_column is pretty focussed on uploading files, not copying them between models.
Enough waffle, let’s just straight to my final clone method:
def clone
result = Gift.new(:name => name, :price => price, :description => description)
unless image.nil?
result.image = File.open(image)
FileUtils.cp(image, result.image)
end
result
end
The main bit of magic here is that I’m passing file_column (on the new object), a file object, rather than just a string. The file_column plugin has some basic support for this, but it’s pretty basic indeed. It seems that it can manage to copy the file out of the temp location into the final destination when I actually save the new model (not done inside clone), but it doesn’t copy it into the temp dir correctly (that normally happens on update. So, I put the copy in myself, and it all works.
You may not want to duplication your images on clone, but obviously this same technique is useful for any copying of file_column files between models, of the same or different model class.
When I get some “free” time (heheh), I’d like to do a bit of work on file column and fix this so that the hack isn’t needed, and perhaps look at some other design improvements. I’ve been through just about every line of it’s code this year on various projects, and while overall I’m greatly impressed, I think there’s more work that could be done to improve it.

Comments
There are 9 comments on this post. Post yours →
It’s using file system, public directory. That’s not very secure.
Of course. This is a public file.
File Column works can be configured to put the files anywhere you like, including private places, but often you’ll find that you want them in public to serve them as web content.
I wondered if you had any experience with file_column working with redbox, regarding the multipart and form_remote_tag. Cheers.
Yeah, this doesn’t work. It’s not a redbox issue, it’s the fact that you can’t use multipart forms with ajax. The solution is to not use ajax, but to submit your form to a hidden iframe instead. The responds_to_parent plugin is necessary for this. I recommend This Article for more information.
The only caveat that I’ve found (and I’ve done this with redbox) is that if you make your target iframe not display (using style=”display: none”) then safari doesn’t work. Instead, set it’s width and height to 1px and remove it’s border, and you should be fine.
Hell, you just saved my day. I have to clone a lot of file_column objects right now.
Regards
def copy_picture(old_id, new_id)
W.find(:all, :conditions => [‘id=?’, old_id]).each { |pic|
picpath = pic.photo
if File.exist?(picpath)
picture = W.new
picture.id = new_id
picture.description = pic.description
picture.photo = File.open(picpath)
picture.update_time = Time.now
picture.save
end
}
end
Craig,
Your clone solution does not seem to work with file_column version 0.3.1.
This blog (in English) provides a modified version of vendor/plugins/file_column/lib/file_column.rb:
http://blog.skrdla.net/2007/09/rails-and-filecolumn-plugin-copy.html
This version is specifically tailored to make your solution work.
Regards,
Onno
Okay, the clone solution DOES work with 0.3.1 as derived from svn (as opposed to the 0.3.1 tar ball posted on the file_column website).
Here’s a small adaptation so you no longer need to copy over all attributes using new():
alias :original_clone :clone
def clone
result = self.original_clone
# we can’t use result.image, we have to
# access the ‘raw’ value of this attribute
result[‘image’] = nil
unless image.nil?
result.image = File.open(image)
FileUtils.cp(image, result.image)
end
result
end
Regards,
Onno
Awesome, thanks Onno.
Post a comment
Required fields in bold.