In this exercise I had Posts with many image attachments attached with attachment_fu and rmagick. I also had a bunch of old data that i wanted to import in using a rake task and finally all image attachments needed to be watermarked.
First my assumptions:
- “A Post can have many Image attachments”
- You have the attachment_fu plugin installed already
Here is how it’s done.
Set constants for your image queue path, the directory for completed imports, and the watermark image path.
Add to bottom of /config/configuration/environment.rb:
def logger RAILS_DEFAULT_LOGGER end # CONSTANTS ####################3 IMAGES_QUEUE_PATH = "#{RAILS_ROOT}/images_import/queue/" IMAGES_DONE_PATH = "#{RAILS_ROOT}/images_import/done/" WATERMARK_IMAGE = "#{RAILS_ROOT}/config/watermark.png"
Configure your /models/image.rb as follows:
class Image :image, :storage => :file_system, #:max_size => 500.kilobytes, #:resize_to => '320x200>', :thumbnails => { :thumb => '320x320>' } validates_as_attachment after_save :watermark_image def watermark_image unless self.thumbnail? image_to_be_watermarked = Magick::Image.read("#{RAILS_ROOT}/public/#{self.public_filename}").first watermark = Magick::Image.read(WATERMARK_IMAGE).first watermarked_image = image_to_be_watermarked.composite(watermark, Magick::SouthEastGravity, Magick::OverCompositeOp) watermarked_image.write("#{RAILS_ROOT}/public/#{self.public_filename}") end end end
Setup /models/post.rb as follows:
class Post :destroy def image_attributes=(image_attributes) image_attributes.each do |attributes| if attributes[:id].blank? images.build(attributes) else image = images.detect { |pic| pic.id == attributes[:id].to_i } logger.debug("image #{attributes[:id].to_i}, is_default as: #{attributes[:is_default].to_i}") image.attributes = attributes end end end end
Create /models/local_file.rb and populate it with this:
require 'tempfile' class LocalFile # The filename, *not* including the path, of the "uploaded" file attr_reader :original_filename # The content type of the "uploaded" file attr_reader :content_type def initialize(path) raise "#{path} file does not exist" unless File.exist?(path) content_type ||= @@image_mime_types[File.extname(path).downcase] raise "Unrecognized MIME type for #{path}" unless content_type @content_type = content_type @original_filename = File.basename(path) @tempfile = Tempfile.new(@original_filename) FileUtils.copy_file(path, @tempfile.path) end def path #:nodoc: @tempfile.path end alias local_path path def method_missing(method_name, *args, &block) #:nodoc: @tempfile.send(method_name, *args, &block) end @@image_mime_types ||= { ".gif" => "image/gif", ".ief" => "image/ief", ".jpe" => "image/jpeg", ".jpeg" => "image/jpeg", ".jpg" => "image/jpeg", ".pbm" => "image/x-portable-bitmap", ".pgm" => "image/x-portable-graymap", ".png" => "image/png", ".pnm" => "image/x-portable-anymap", ".ppm" => "image/x-portable-pixmap", ".ras" => "image/cmu-raster", ".rgb" => "image/x-rgb", ".tif" => "image/tiff", ".tiff" => "image/tiff", ".xbm" => "image/x-xbitmap", ".xpm" => "image/x-xpixmap", ".xwd" => "image/x-xwindowdump" }.freeze end
Now we create a new rake file and task to do our importing.
Create /libs/tasks/import.rake
desc "Imports images/post content from set constant path" task :do_gallery_import => :environment do require 'logger' ignore = ['.','..','body.txt','title.txt'] directory = Dir.new(IMAGES_QUEUE_PATH) directory.each do |image_dir| unless ignore.include?(image_dir) gallery_path = IMAGES_QUEUE_PATH + image_dir logger.debug("Importing: " + gallery_path) if !create_new_post(gallery_path).nil? logger.debug("Import Complete.") #Move the completed import dir move_done(image_dir) end end end end #Creates a new post by reading the title.txt, body.txt and all images def create_new_post(directory) p = Post.new p.title = File.new(directory + "/title.txt").read.chomp p.body = File.new(directory + "/body.txt").read.chomp get_pics_in_directory(directory).each do |image| p.image_attributes=[{"uploaded_data"=>LocalFile.new(image)}] end p.save end #Returns a list of all images in a "working" directory def get_pics_in_directory(directory) ignore = ['.','..','body.txt','title.txt'] file_list = [] image_directory = Dir.new(directory) image_directory.each do |pic| unless ignore.include?(pic) file_list << directory + "/#{pic.to_s}" end end return file_list end def move_done(imported_directory) gallery_path = IMAGES_QUEUE_PATH + imported_directory gallery_done_path = IMAGES_DONE_PATH + imported_directory system "mv #{gallery_path} #{gallery_done_path}" logger.debug("Moved #{gallery_path} to #{gallery_done_path}") end
Create the following directories:
/images_import/done/
/images_import/queue/