saving image dimensions with file_column
A minor UI detail in development required that we include the width and height of certain images in the HTML and XML views. Since we were using the file_column plugin, which doesn’t normally save any image information besides the file name, this turned out to require some hacking.
file_column does a great job of making it easy to associate image upload fields with forms, and save the resulting images with a model. You can save various versions as well, manipulated in many ways using Rmagick – a large, small, micro-thumb, for instance, and there are convenient validation hooks. And the plugin, though lightly documented, is simple and easy to understand by reading the code.
But – file_column does not save the dimensions of the resulting images, a problem for us and at least a few others. Usually, this is not an issue, but in order to optimize the layout of a certain Flash-based UI, we needed to tell Flash via it’s XML setup file the dimensions of the images it was supposed to display.
After various false starts and dead ends, we created the following additions to the plugin that will cause it to save the image height and width to the database, given only columns of the correct name.
Add the following hooks to lib/file_column.rb around line 687, which will cause the model to look for a before_save method in any of it’s subclasses (this mimicks the existing technique used to create the after_save hook and such):
before_save_method = "#{attr}_before_save".to_sym define_method before_save_method do instance_variable_set state_attr, send(state_method).before_save end before_save before_save_method
then, add this method to lib/magick_file_column.rb around line 68. This will look for model methods of the appropriate names, and if found, will read image information from the file on disk and update the model attributes accordingly.
def before_save if just_uploaded? if @instance.respond_to? "#{@attr}_img_width" or @instance.respond_to? #{@attr}_img_height" img = ::Magick::Image::read(absolute_path).first if @instance.respond_to? "#{@attr}_img_width=" @instance.send("#{@attr}_img_width=", img.columns) end if @instance.respond_to? "#{@attr}_img_height=" @instance.send("#{@attr}_img_height=", img.rows) end end end self end
this will cause the plugin to look for a X_img_height and X_img_width method in the model, where X is the base name of your file column.
So, given a basic file_column setup such as
class User < ActiveRecord::Base file_column :avatar, :magick => { :geometry => "70x70" } end
then you can simply add columns named ‘avatar_img_width’ and ‘avatar_img_height’ to your ‘users’ table. The plugin will now automatically save the width and height of the image to those columns.
A migration which would accomplish this might look like:
class AddUserAvatarDimensions < ActiveRecord::Migration def self.up add_column :users, :avatar_img_width, :int add_column :users, :avatar_img_height, :int end def self.down remove_column :users, :avatar_img_width remove_column :users, :avatar_img_height end end
Known issue: if you use file_column to save multiple sizes of the given image, this will only save the dimensions of the last one. Further work would be needed to include the name attribute of the image size in the column name.