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
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!
9 Comments
This was a great help. Thanks for post.
-Mike (guy you met at onAIR who had the music site)
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.
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
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.
Hi,
I’ve integrated these changes you’ve made into the Ruboss “Flexible Rails” framework to allow file uploading (as well as Restful_Authentication) to be possible from Flex to Rails. Thanks for all of your help. I haven’t completely finished the Flex part yet, but this tutorial was great.
Here’s the beginnings of a RESTful Flex on Rails social networking site. Just laying the groundwork a little bit. Ruboss Tutorial
Peace,
Lance
It seems lot of people have been able to successfully make use of this blog.
unfortunately i’m still struggling. my questions:
1. how does the rails controller code look like? how are you guys constructing model object out of posted data?
thx
does this hack even work with latest version of fu plugin anymore????
nothing seems tobe work for me
Alan,
It works. attachment_fu requires default naming of some form variables to make things work. Be certain that your incoming parameters hash has a filename and uploaded_data parameter set. In Flex, you can set the name of your uploaded file with
file.upload(request, “uploaded_data”);
where file is a FileReference object.
A little bit to late, but you could use
content_type = `file –brief –mime “#{File.join(temp_path)}”`.chomp
Because option `file -bi …`
returns ‘regular_file’ instead of something like ‘image/jpeg’ on Mac OS-X
One Trackback
[...] Hacking attachment_fu to work with Flash/Flex uploads and crop square images [...]