Isnor Creative
Isnor Creative Blog
Ruby, Ruby on Rails, Ember, Elm, Phoenix, Elixir, React, Vue

Aug 18, 2008

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.

Gordon B. Isnor

Gordon B. Isnor writes about Ruby on Rails, Ember.js, Elm, Elixir, Phoenix, React, Vue and the web.
If you enjoyed this article, you may be interested in the occasional newsletter.

I am now available for project work. I have availability to build greenfield sites and applications, to maintain and update/upgrade existing applications, team augmentation. I offer website/web application assessment packages that can help with SEO/security/performance/accessibility and best practices. Let’s talk

comments powered by Disqus