Rails Version 4.0 and Up
Required Dependencies
  • dropzonejs-rails — 0.4.16
  • paperclip — 4.2.0

Introduction

DropzoneJS is a handy javascript library for allowing multiple file uploads via AJAX. It features drag and drop support, folder support, and much more on browsers that support these features. This article will show you how to implement multiple image uploads directly to paperclip using DropzoneJS. You can just as easily use these steps to upload files to CarrierWave or some other file upload handler. Let's get started.

Rails Application Setup

The first thing we will do is add couple gems to our Gemfile. The dropzonejs-rails gem is simply a helper gem that cleanly integrates DropzoneJS into our Rails app. The paperclip processes image uploads. If you aren't familiar with Paperclip, you may want to read our basic Paperclip tutorial at http://richonrails.com/articles/getting-started-with-paperclip. Otherwise, open up your Gemfile and add in the lines listed below.

Gemfile:

gem 'paperclip', '~> 4.2.0'
gem 'dropzonejs-rails', '~> 0.4.16'

Now let's run a bundle install to install the gems.

Terminal Commands:

bundle install

Now let's create a model to store our image information for Paperclip. For this example app we will create a model called Image with an attachment called avatar. You can name the attachment something else, just be sure to modify your code appropriately. run the command below to create the image model and migrate the database now.

Terminal Commands:

rails g model image avatar:attachment
rake db:migrate

Now let's add some code to our Image model to tell paperclip we want to have an attachment attached. Open up your image model and add in the code listed below.

app/models/image.rb:

class Image < ActiveRecord::Base
  has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
  validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
end

Now let's create a controller called Images which will be used to display and allow the upload of your images. Run the command below to create this controller now.

Terminal Commands:

rails g controller images index create

Now let's update our routes file to properly set up the routes for our images controller. Open up your routes file and modify it so that it looks like the code listed below.

config/routes.rb:

Rails.application.routes.draw do
  resources :images, only: [:index, :create]
  root to: "images#index"
end

Now let's modify our Images controller to add logic to handle the file upload as well as listing each of the images. Open up the Images controller and modify it so that it looks like the code listed below.

app/controllers/images_controller.rb:

class ImagesController < ApplicationController
  def index
    @images = Image.all
    @image = Image.new
  end


  def create
    @image = Image.new(image_params)

    if @image.save
      render json: { message: "success", fileID: @image.id }, :status => 200
    else
      render json: { error: @image.errors.full_messages.join(',')}, :status => 400
    end     
  end
private
  def image_params
    params.require(:image).permit(:avatar)
  end
end

Dropzone expects a json return, so the create method returns a JSON success or failure based on whether the image was uploaded successfully or not.

Now let's add Bootstrap to our application to pretty things up a bit. Open up your application layout and modify it so that it looks like the code listed below.

app/views/layouts/application.html.erb:

<!DOCTYPE html>
<html>
<head>
  <title>MultipleImageUploadExampleApp</title>
  <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
  <%= stylesheet_link_tag    'http://yandex.st/bootstrap/3.1.1/css/bootstrap.min.css', media: 'all', 'data-turbolinks-track' => true %>
  <%= javascript_include_tag 'http://yandex.st/bootstrap/3.1.1/js/bootstrap.min.js', 'data-turbolinks-track' => true %>
  <%= csrf_meta_tags %>
</head>
<body>

<%= yield %>

</body>
</html>

Great, now let's create our views. First let's create the index view. Open up your index view for the images controller and modify it so that it looks like the code listed below.

app/views/images/index.html.erb:

<h1>My Images</h1>
<%= form_for(Image.new, html: { multipart: true, class: "dropzone"}) do |f|  %>
  <div class="fallback">
    <%= f.file_field :avatar %><br>
    <%= f.submit "Upload my Avatar" %>
  </div>
<% end %>

<div class="index">
  <%= render "index" %>
</div>

Great, now we need to add some JavaScript to tell Rails how to handle the remote ajax file processing that we will do using dropzone. Create a view called index.js.erb for your images controller and add in the code listed below.

app/views/images/index.js.erb:

$(".index").html("<%= escape_javascript(render('index')) %>")

Great, now let's create the partial we reference in the previous code. Create a new partial called _index.html.erb for your images controller and add in the code listed below.

app/views/images/_index.html.erb:

<% @images.each do |image| %>
  <div class="img-thumbnail"><%= image_tag image.avatar.url(:thumb), alt: image.avatar.url(:thumb) %></div>
<% end %>

Now let's modify our application.css and add the dropzone css require. Open up your application.css file and modify it so that it looks like the code listed below.

app/assets/stylesheets/application.css:

/*
 *= require_tree .
 *= require dropzone/dropzone
 *= require_self
 */

Great, now let's add a bit more JavaScript to finish things up. Open up your application.js file and add in the code listed below.

app/assets/javascripts/application.js:

$(document).ready(function(){
	// disable auto discover
	Dropzone.autoDiscover = false;
 

	var dropzone = new Dropzone (".dropzone", {
		maxFilesize: 256, // Set the maximum file size to 256 MB
		paramName: "image[avatar]", // Rails expects the file upload to be something like model[field_name]
		addRemoveLinks: false // Don't show remove links on dropzone itself.
	});	

	dropzone.on("success", function(file) {
		this.removeFile(file)
		$.getScript("/images")
	})
});

The code above creates a new dropzone. paramName sets the parameter name to a format Rails can understand and parse. You can modify this to suit your needs. maxFileSize sets a maximum file size of 256 MB. Setting addRemoveLinks to false hides the remove links that dropzone creates. We also create a success handler for dropzone that will remove the image preview from dropzone and do an ajax refresh of our image index.

If you start your Rails server and navigate to http://localhost:3000 you will notice that you can drag and drop images onto the app. On certain browsers, such as Google Chrome, you can even drag and drop one or more folders of images onto the dropzone placeholder and have them upload. In addition you can also click the dropzone and select a file via the file selection screen. Further documentation, including information on the various callbacks as well as customizing the look of the Dropzone itself can be found at the Dropzone homepage. That's it! Thanks for reading!

Resources