Ruby on Rails Uploads Multiple Files With DropzoneJS and Paperclip Gem

Ruby on Rails Uploads Multiple Files with DropzoneJS and Paperclip Gem

DropzoneJS is a 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.

In this article I will show you how to implement multiple images files uploads directly to paperclip using DropzoneJS. Let’s run through this with me.

Create Rails Project
To create a Rails project; open up your terminal and type commands below:

1
rails new dropzone -d mysql

Add Gems
We will add two gems to our Gemfile. dropzonejs-rails gem is a helper gem that integrates DropzoneJS into our Rails app. paperclip for processing image uploads.

Open up your Gemfile and add in the lines listed below:

Gemfile
1
2
gem "paperclip", "~> 4.2"
gem 'dropzonejs-rails'

Now let’s run a bundle install to install the gems:

1
bundle install

Create a Image Model
Now we will create a model to store our image information for Paperclip. Run the command below to create the image model and migrate the database:

1
2
rails g model image avatar:attachment
rake db:migrate

Then add some code to Image model to tell paperclip we want to have an attachment attached. Open up your image model (app/models/image.rb:) and add the code listed below:

image.rb
1
2
3
4
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

Create a Images Controller
Then create an Images controller which will be used to display and allow the upload of our images. Run the command below to create this controller:

1
rails g controller images index create

Then update our routes file to set up the routes for our images controller. Open up the routes file (config/routes.rb) and modify it:

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

Then 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 (app/controllers/images_controller.rb) and modify it:

images_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

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

Then add Bootstrap to our application. Open up your application layout (app/views/layouts/application.html.erb)and modify it:

application.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html>
<head>
  <title>DropzoneJS</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>

Well, then create our views. First let’s create the index view (app/views/images/index.html.erb). Open up your index view for the images controller and modify it:

index.html.erb
1
2
3
4
5
6
7
8
9
10
11
12
<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>

Then 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 app/views/images/index.js.erb for your images controller and add the code listed below:

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

Then create the partial that we reference in the previous code. Create a new partial called app/views/images/_index.html.erb for your images controller and add the code listed below:

_index.html.erb
1
2
3
4
5
<% @images.each do |image| %>
  <div class="img-thumbnail">
    <%= image_tag image.avatar.url(:thumb), alt: image.avatar.url(:thumb) %>
  </div>
<% end %>

Then modify our application.css and add the dropzone css require. Open up your app/assets/stylesheets/application.css file and modify it:

application.css
1
2
3
4
5
/*
 *= require_tree .
 *= require dropzone/dropzone
 *= require_self
 */

Then modify our application.js and add the dropzone js require. Open up your app/assets/javascripts/application.js file and modify it:

application.js
1
/= require dropzone

Then add a bit more JavaScript to finish things up. Open up your app/assets/javascripts/images.js file and add in the code listed below:

images.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$(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");
  })
});

So far so good, 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. That’s it! See ya! :)