Rails Image Uploads Model

Monday 18 August 2008

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.

Comments

Add A Comment

Previous Articles

Wed 10 Dec 2008

Translates To French

Sat 15 Nov 2008

Acts As Draftable