Ruby on Rails Image Uploads Model
I have a magazine site with a fair number of models containing uploaded image columns. I started with a chunk of code in each model to handle the image save eg @album.save_image(@image). It was working fine but it didn’t feel right, since each model contained the exact same code for handling the save, it seemed to go completely in the face of all the noble, DRY Rails way. It occurred to me that the thing to do was refactor to a generic ImageSave model that could be called by any controller. This is what I came up with. It’s been working well for me. It’s stores everything by year and month. Might be a good idea to put the year folders in a subdirectory called uploads just to keep them isolated from other site images.
app/models/image_save.rb
class ImageSave
def self.save_image(image)
@filename = image.original_filename
@directory = save_directory
do_save(@directory,@filename,image)
return @filename
end
def self.replace_image(image,item)
@filename = image.original_filename
@directory = replace_directory(item)
do_save(@directory,@filename,image)
return @filename
end
def self.do_save(directory,filename,new_image)
FileUtils.mkdir_p(directory)
# create the file path
path = File.join(directory, filename)
# write the file
File.open(path, "wb") { |f| f.write(new_image.read) }
end
def self.directory
"public/images"
end
def self.save_directory
directory + '/' + Date.today.year.to_s + '/' + Date.today.month.to_s
end
def self.replace_directory(item)
directory + '/' + item.created_at.year.to_s + '/' + item.created_at.month.to_s
end
end
Controllers
def create
@album = Album.new(params[:album])
@album.image = ImageSave.save_image(params[:album][:image])
@album.save
etc..
def update
@album = Album.find(params[:id])
if params[:album][:image]
if not params[:album][:image].length > 1
params[:album].delete('image')
else
params[:album][:image] = ImageSave.replace_image(params[:album][:image],@album)
end
end
@album.update_attributes(params[:album])
etc...
So the idea here is that for a new record you set the image attribute to the value returned by the save image method - you pass method that method the form parameter for the image file when you call it. For an updated image, you check to see if they have set a new image in the form, checking the length of that param, if it is not greater than 1 you remove that param entirely, otherwise you set the params value now to the value returned by the replace_image method, which this time gets passed the param, and the object it is acting upon. The difference here is that you will use the original item created_at date as a reference for where to put the file, rather than the time now for a new object.
I am available for Ruby on Rails consulting work – get in touch to learn more.