Hacking attachment_fu to work with Flash/Flex uploads and crop square images
Rick Olson’s attachment_fu is my favorite file upload plug-in because let’s you use three different image manipulation tools [rmagick, mini-magick, image science] and storage options [file system, database, amazon s3]. However it doesn’t yet support two features I use on every CMS I build, Flash/Flex file upload (images will upload but won’t be resized) and square image cropping. Here’s how to tweak it to get both features working.
First up, support for Flash/Flex upload (I should really drop the ‘Flash/’ part as I only use Flex now) , first up Flex upload… Ilya Devers posted the solution on Google groups, but I get to claim 1% credit as my blog is mentioned in his post :P
The problem is really on the Flex side of things as all uploads come through as ‘application/octet-stream’ for the mime-type. attachement_fu can upload any kind of file so it checks the mime-type before running it’s resize code, since it’s looking for an image it skips over the Flex uploaded files. Ilya’s rather ingenious solution is to override attachment_fu and use the file system to check the file type. To overide attachment_fu add the ‘uploaded_data=’ and ‘get_content_type’ methods to your upload model.
end
Next is cropping square images with mini-magick. Currently if you request a square image attachment_fu will stretch rather than crop the image, this time I’ll ‘borrow’ the solution from Craig Ambrose. This time you have to dig deeper down into the depths of the rails plugins directory to edit ‘vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/mini_magick_processor.rb’ and replace the resize_image method with the following.
# Performs the actual resizing operation for a thumbnail
size = size.first if size.is_a?(Array) && size.length == 1
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
if size.is_a?(Fixnum)
resize_and_crop(img, size)
else
size[0] == size[1] ? resize_and_crop(img, size[0]) : img.resize(size.join(‘x‘))
end
else
img.resize(size.to_s)
end
self.temp_path = img
end
if image[:width] < image[:height]
shave_off = ((image[:height] - image[:width])/2).round
image.shave(“0x“)
elsif image[:width] > image[:height]
shave_off = ((image[:width] - image[:height])/2).round
image.shave(“x0“)
end
image.resize(“x“)
return image
end
To crop an image you use ‘‘ as in the Model code above. That’s it!
January 14th, 2008 at 2:39 pm
This was a great help. Thanks for post.
-Mike (guy you met at onAIR who had the music site)
January 16th, 2008 at 3:22 pm
Hey Alastair! Nice write up. I wish I would have seen your write up for squaring thumbnails. I ended up having the Flash guys do some masking magic on the images, but this would have been better. Oh well. And it’s nice to know there are other people out there who just want to push their apps out there in the best, fastest, easiest way possible and get on with the next app. :)
February 15th, 2008 at 5:51 pm
I made the following modification for my purposes:
def resize_image(img, size)
logger.debug(”Size: #{size.inspect}”)
size = size.first if size.is_a?(Array) && size.length == 1
if size.is_a?(Fixnum) || (size.is_a?(Array) && size.first.is_a?(Fixnum))
if size.is_a?(Fixnum)
resize_and_crop(img, size)
else
#size[0] == size[1] ? resize_and_crop(img, size[0]) : img.resize(size.join(’x'))
#resize_and_crop_irregular(img, size[0], size[1])
size[0] == size[1] ? resize_and_crop(img, size[0]) : resize_and_crop_irregular(img, size[0], size[1])
end
else
img.resize(size.to_s)
end
self.temp_path = img
end
def resize_and_crop_irregular(image, nu_width, nu_height)
original_ratio = (image[:width].to_f / image[:height].to_f).to_f
nu_ratio = (nu_width.to_f / nu_height.to_f).to_f
if nu_ratio original_ratio
new_ratio = (nu_width.to_f / nu_height.to_f).to_f
corrected_height = (image[:width].to_f / new_ratio).to_f
shave_off = ((image[:height] - corrected_height)/2).round
image.shave(”0x#{shave_off}”)
end
image.resize(”#{nu_width}x#{nu_height}”)
return image
end
April 19th, 2008 at 11:31 am
Very helpful, I’ve not had so much trouble working with Flex and anything else since I started trying to upload files, there were so many issues and this was just one of them.
However your fix didn’t work for me, it always called the rescue block, so I commented that out and then did some dumping of the various variables, the results for content_type (after the file -bi “#{File.join(temp_path)} part was):
ERROR: cannot open `â??/tmp/CGI4338-1â??’ (No such file or directory)
I had no idea what was going on as the temp_path was:
/tmp/CGI4338-1
I couldn’t figure this out so I added the following hack to the rescue block:
content_type = Mime::Type.lookup_by_extension( File.extname( self.filename ).gsub( /\./, ” ) )
if content_type.to_s.match( /.*\/.*/ )
content_type.to_s
else
fallback
end
I don’t really like this, but it works (I’ve registered the appropriate mime types with Mime::Type.register). Do you have any idea what that error is on the file -bi line.
Anyway it was still a great help to come across this post as it didn’t mean I spent as long figuring out why the thumbnails weren’t being created.
Thanks again.