Multiple file upload with attachment_fu
Hi and welcome to the first tutorial with Ruby on Rails. Today we are going to look at a file upload plugin called attachment_fu together with javascript to handle a multiple file upload form. We are not going to go into details about attachment_fu plugin nor every detail on Ruby on Rails. We aim low and see where we end up.
To use the attachment_fu plugin we need to install an image processor. I have been using ImageMagic. Much more information about how to install image processors and use attachment_fu can be found at the link below:
http://clarkware.com/cgi/blosxom/2007/02/24#FileUploadFu
Let’s continue, first we install the attachment_fu plugin:
ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/attachment_fu/
When this is done the attachment_fu plugin will be installed in our project. Next, we need to download the javascript file written by Stickman. URL found below, make sure you don’t download the compressed file:
http://the-stickman.com/web-development/javascript/upload-multiple-files-with-a-single-file-element/
Place the javascript file in your public/javascripts folder in your Ruby on Rails project. The view files that will use this javascript need to include it:
<%= javascript_include_tag 'multifile' %>
This will load the multifile.js file when the page is accessed. You can also add this in application.html.erb view file if you are using layouts. Then you don’t have to apply it to each new view file using the script. Next, open the multifile.js file in a editor. We are going to change a few lines.
The very first change is in the function MultiSelector. Instead of two arguments we will pass in three. The function head should look like this:
function MultiSelector(list_target, name, max) { ...
A few lines down we are setting the element.name attribute. Change this to:
element.name = name;
That’s it. What we have done is we are now able to set a name on the upload fields we are using. The Multifile javascript works as follows, simplified: The browser will only show one file field at a time. When we add something in the file field it will set the display attribute to none and show a new file field. That means, even though only one file field is showing we can upload as many files as we want. With our little change in the javascript file we can set the name as an array, similar to checkboxes and radioboxes. This array of files can we later iterate through and upload. This works both for PHP and Ruby on Rails, but since we are using the attachment_fu plugin to upload our files we continue our implementation in Ruby on Rails.
So, now we have attachment_fu plugin installed and the MultiFile javascript downloaded and modified. Next, we setup a scenario. We have products and each product can have multiple files. For this we scaffold product and the images. Oh, for a quick tip on the side, I recommend using Ryan Bates nifty_scaffolding plugin. It’s superb. It’s a very useful gem, http://github.com/ryanb/nifty-generators/tree/master.
Now, when you have created your models, controllers and views go to the products view folder and open _form.html.erb. It’s a partial. Since we are using file fields we need to set multipart to true. Todo this, make sure your form_for tag looks like this:
<% form_for @product, :html => { :multipart => true } do |f| %>
Remember to include the javascript file to this file if you’re not using your default layout view file.
After this change we add code specific to the MultiFile script:
<%= file_field 'image', 'data' %><br/>
<div id="files_list">Files (maximum 15):</div>
<script>
var multi_selector = new MultiSelector(document.getElementById('files_list'), 'product[attachment_data][]', 15);
multi_selector.addElement(document.getElementById('image_data'));
</script>
The first line inside of the <script> element is updated to fit our purpose. We name each new file field product[attachment_data][]. Hopefully this will work as we talked about before, as an array of files. Notice how the file_field must be named image_data and the div must have the id of files_list or you need to update the new names in the <script> element.
Now if you go to this page, and everything is correct, you will be able to add multiple files. Each file name will be listed in the div#files_list and a new file field will be displayed after each image.
Now we turn back on how we named the file fields. The attachment_data attribute is just a virtual attribute. When we call @product.save in our controller we need to handle the attachment_data attribute. To do that, we open our product model and add:
after_update :save_attachments
validates_associated :product_images
def attachment_data=(attachment_data)
attachment_data.each do |file|
product_images.build({:uploaded_data => file}) unless file == ""
end
end
def save_attachments
product_images.each do |img|
img.save
end
end
The after_update statement saves each image IF we managed to save the product correctly. That’s what the save_attachments function does. The attachment_data=(attachment_data) function will be called when @product.save is called. The variable attachment_data will include all information necessary to upload each file/image. Hopefully this isn’t really hard to understand. A very useful tutorial on virtual attributes can be found below:
http://railscasts.com/episodes/16-virtual-attributes
And now we are done! When we submit our form in the view file the @product.save function will be called and our virtual attribute attachment_data will be set and build each file/image we are uploading. If the product itself is valid, it will be saved and call the after_update function to save our files/images. If you look at the links I provided this shouldn’t be complicated. Basically the only thing we did was to modify the javascript a little bit to make it more suitable for us.
Hope this will be useful! I had a lot of trouble finding a good solution to it and this works very well. Bye!
About this entry
You’re currently reading “Multiple file upload with attachment_fu,” an entry on Is the coffee still warm?
- Published:
- April 13, 2009 / 1:13 pm
- Category:
- Uncategorized
8 Comments
Jump to comment form | comment rss [?] | trackback uri [?]