skater coder

angela chng – Ruby on Rails / PHP web developer & skater girl in Singapore

  • blogabout stuff
  • aboutthis website
  • contactdrop a message

August 31, 2010
Posted by roadburn

How to upload multiple files on rails (uploadify + paperclip + jquery)

Setting Up

Download Uploadify from
http://www.uploadify.com/

copy the uploadify files into the following directories

1
2
3
4
5
6
7
public/javascripts/uploadify/jquery.uploadify-2.1.0.min.js
public/javascripts/uploadify/uploadify.swf
public/javascripts/uploadify/swfobject.js
 
public/images/cancel.png
 
public/stylesheets/uploadify.css

Create this file
app/middleware/flash_session_cookie_middleware.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require 'rack/utils' 
class FlashSessionCookieMiddleware 
  def initialize(app, session_key = '_session_id') 
    @app = app 
    @session_key = session_key 
  end 
 
  def call(env) 
    if env['HTTP_USER_AGENT'] =~ /^(Adobe|Shockwave) Flash/ 
      req = Rack::Request.new(env) 
      env['HTTP_COOKIE'] = "#{@session_key}=#{req.params[@session_key]}".freeze unless req.params[@session_key].nil? 
    end 
    @app.call(env) 
  end 
end

Add this line to
config/initializers/session_store.rb

1
ActionController::Dispatcher.middleware.insert_before(ActionController::Base.session_store, FlashSessionCookieMiddleware, ActionController::Base.session_options[:key])

Make sure your environment.rb file has this

config/environment.rb

1
2
3
4
5
6
7
8
9
Rails::Initializer.run do |config|
 
  config.gem 'mime-types', :lib => 'mime/types'
  config.gem 'paperclip' 
 
  %w(middleware).each do |dir| 
    config.load_paths << "#{RAILS_ROOT}/app/#{dir}" 
  end
end

Models

Let’s assume you want to create a photo album with many photos.

Create your photo model with the following:

db/migrate/create_photos.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CreatePhotos < ActiveRecord::Migration
  def self.up
    create_table :photos do |t|
      t.integer :attachable_id
      t.string :description  
      t.string :data_file_name  
      t.string :data_content_type  
      t.integer :data_file_size  
      t.datetime :data_updated_at
      t.string :parent  
    end
  end
 
  def self.down
    drop_table :images
  end
end

In your album model:

app/model/album.rb

1
has_many :photos, :dependent => :destroy, :foreign_key => :attachable_id

In your photo model:
app/model/photo.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
belongs_to :album, :foreign_key => :attachable_id
 
has_attached_file :data, 
      :url => "/assets/images/:id/:style/:basename.:extension",
      :path => ":rails_root/public/assets/images/:id/:style/:basename.:extension",
      :styles => { 
      :large => "800x800>", 
      :medium => "300x300>", 
      :thumb => "100x100#"}
 
validates_attachment_presence :data
validates_attachment_size :data, :less_than => 2.megabytes  
validates_attachment_content_type :data, :content_type => ['image/jpeg', 'image/jpg', 'image/png', 'image/gif']

Controllers

app/controllers/photos_controller.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class PhotosController < ApplicationController 
  def create
 
    newparams = coerce(params) 
    @photo = Photo.new(newparams[:upload]) 
 
    // so this photo can belong to other models other than album
    @parent = @photo.parent.constantize.find(@photo.attachable_id)
 
    if @photo.save
      flash[:notice] = "Successfully uploaded photo."
      respond_to do |format| 
        format.html {redirect_to @parent} 
        format.json  { render :json => { :result => 'success', :upload => photo_path(@photo) } } 
      end 
    else
      flash[:notice] = "Only gif, jpg or png files allowed"
      respond_to do |format| 
        format.html {redirect_to @parent} 
        format.json  { render :json => { :result => 'failed'} } 
      end
    end
 
  end
 
  def show
    @photo = Photo.find(params[:id])    
  end
 
  def coerce(params) 
    if params[:upload].nil? 
      h = Hash.new 
      h[:upload] = Hash.new 
      h[:upload][:attachable_id] = params[:attachable_id] 
      h[:upload][:parent] = params[:parent] 
      h[:upload][:data] = params[:Filedata] 
      h[:upload][:data].content_type =  MIME::Types.type_for(h[:upload][:data].original_filename).to_s 
      h 
    else 
      params 
    end 
  end
end

Views

app/views/albums/show.html.erb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<%= stylesheet_link_tag "uploadify"  %>
<%= javascript_include_tag "uploadify/swfobject", "uploadify/jquery.uploadify.v2.1.0.min.js" %>
 
<%- session_key_name = ActionController::Base.session_options[:key] -%>
 
<script type="text/javascript" charset="utf-8"> 
jQuery.noConflict();
 
jQuery().ready(function() { 
   jQuery('#photo_upload').click(function(event){ 
      event.preventDefault(); 
   }); 
 
  jQuery('#photo_upload').uploadify({ 
 
    'uploader'    : '/javascripts/uploadify/uploadify.swf', 
    'script'      : '/cms/photos', 
    'multi'       : true, 
    'cancelImg' : '/photos/cancel.png',
 
    onComplete : function(event, queueID, fileObj, response, data) { 
	var dat = eval('(' + response + ')'); 
	if (dat.result=='success'){
 	  jQuery.getScript(dat.upload);
        }else{
	  alert("Only .jpg, .gif, .png files under 2MB allowed. ")
	}
    }, 
 
    'scriptData' : { 'format' : 'json', 
      '<%= session_key_name %>' : encodeURIComponent('<%= u cookies[session_key_name] %>'), 
      'authenticity_token' : encodeURIComponent('<%= u form_authenticity_token if protect_against_forgery? %>'), 
      'attachable_id' 	: '<%= @album.id %>',
      'parent' 	: 'Album'} 
    }); 
    jQuery('#photo_submit').click(function(event){ 
      event.preventDefault(); 
      jQuery('#photo_upload').uploadifyUpload(); 
    }); 
  }); 
</script>
 
<% form_for Image.new(:attachable_id => @deal.id), :html => { :multipart => true } do |f| %>
	<div class="image-uploader">
		<%= f.file_field :data, :id => 'image_upload'  %>
		<%= f.submit "Upload Images", :id => 'image_submit'%>
	</div>
<% end %>

Resources

Many thanks to the following articles that helped me figure out how to get this working
Please follow the links for more detailed explanations

http://fencore.posterous.com/uploadify-with-paperclip-on-rails-tutorial
http://timmyc.posterous.com/uploadify-on-rails-with-paperclip
http://www.practicalecommerce.com/blogs/post/432-Multiple-Attachments-in-Rails
http://www.utoronto.ca/web/htmldocs/book/book-3ed/appb/mimetype.html
http://en.wikipedia.org/wiki/Internet_media_type

2 Comments

Posted Under Coding Tips Rails

2 Comments

stmurder
December 9, 2010

wow, this is awesome!;)

thank you

hollownest
January 8, 2011

This was very helpful, thanks for posting. Some updates are needed for Rails 3.0, and I had ferreted them out, but then I found a gem that handles all of the Rails side:

https://github.com/trevorturk/flash_cookie_session

Also, to work with the Uploadify 3.0 beta, there are a few changes:

$(‘#photo_upload’).uploadify({
‘swf’ : ‘/javascripts/uploadify/uploadify.swf’,
‘uploader’ : ‘/assets’,
‘checkExisting’: ‘/assets/exists’,
‘cancelImage’ : ‘/images/uploadify-cancel.png’,
‘folder’ : ‘/tmp’,
‘auto’ : true,
‘postData’ : {
‘_http_accept’: ‘application/javascript’,
‘_method’: ‘put’,
‘#{Rails.application.config.session_options[:key]}’ : ‘#{cookies[Rails.application.config.session_options[:key]]}’,
‘#{request_forgery_protection_token}’: ‘#{form_authenticity_token}’
}

/assets/exists is called with: parameters => {filename”=>”hachiko.jpg”} but I haven’t yet decided exactly how to handle it.

Leave a comment

* = Required

  • CATEGORIES
    • Coding Tips
    • ExtJS 4
    • Food
    • Portfolio
    • Rails
    • Sharepoint
    • Ubuntu
    • Wordpress

  • TAGS
    activities AD ADAM ajax architecture authentication bluehost books custom application custom form date design disk partition ECTS event id eventid moss forum guid hotfix iis imap keberos list id lists masterpage moss mysite profiles Rails rails 2.0 reporting services ruby Sharepoint sharepoint 2007 site templates site usage sql server 2005 time updates vhd virtual pc visual studio webparts workflow wss

Subscribe via RSS