Ajax file upload with DropezoneJs and Paperclip - Rails

Ajax file upload with DropezoneJs and Paperclip - Rails

___

In this short tutorial, I will share how to achieve an ajax multiple file upload in Ruby on Rails using an awesome JavaScript library called Dropezone.

Dropzone is an easy to use drag 'n' drop library that supports image previews and shows nice progress bars. It is a light weight JavaScript library that turns an HTML element into a 'dropzone'. This means that a user can drag and drop a file onto it, and the file gets uploaded to the server via AJAX. Sometime in one of your projects, you will need to give the user the ability to upload multiple images at once and instantly see visual feedback on the upload progress and/or errors that come up during the upload.

You can check out a Live Demo of what we will be creating. You can also check out the full source code used to create this tutorial at my github repo

Preparation

We will start by grabbing DropzoneJs from the Github Repository. The most important files for this tutorial include:

  • dropzone.min.js
  • dropzone.css (in css folder)
  • spritemap.png and spritemap@2x.png (from the images folder)

With that we are ready to plug-in dropzone in our application. I will be creating our application from scratch while trying to keep it as simple as possible.

Getting Started

Lets create a new rails application from our console. Lets name it 'dropzone-rails'. You can give it whichever name you wish.

$ rails new dropzone-rails

To handle the uploading of the images, we will be using the famous and robust Paperclip Gem which is as an easy file attachment library for Active Record. We then add the gem in our Gemfile and run bundle to install it. I've also added the bootstrap gem to give me some quick styling.

Gemfile

Loading Gist

Integrating Dropzone

  1. Copy (spritemap.png and spritemap@2x.png ) to our app/assets/images directory of our rails application
  2. Next add dropzone.css and dropzone.min.js to vendor/assets/stylesheets and vendor/assets/javascript folders respectively. Don't forget to setup the correct reference path for our image files in dropzone.css stylesheet
  3. Next up, lets require our dropzone css file in our css manifest file and dropzone.min.js in our javascript manifest file

application.css

Loading Gist

application.js

Loading Gist

Next lets create a model to handle our uploaded images. For my case I will name the model upload. It can be whatever name you choose to give it. My model will only contain attributes of images which we will add shortly using the paperclip generator. Generating our model....

$ rails g model upload

We then add paperclip attachments to our upload model. To do this, simply run the paperclip generator as so

$ rails g paperclip upload image

If this seems a little bit confusing, check out this awesome railscast on how to use paperclip. Those to commands create migration files which we can run rake db:migrate to migrate our database and add the respective tables and columns.

The next step is to update the model code. We need to use has_attached_file to tell it the name of the attachment field we specified when we ran the migration. I have also done a couple of validations which you can tweak as you wish

upload.rb

Loading Gist

Next up, lets create a controller uploads with the new action which will interact with the user and enable him/her upload images and save them to the database.

$ rails g controller uploads new

With this, it would be a good idea to make the root of our application point to the new action of our uploads controller. We can also add an upload resource to our routes.rb file which will provide several RESTFUL urls.

routes.rb

Loading Gist

Visiting http://0.0.0.0:3000/ should render the new view of our uploads controller

Its now time to add our Dropzone goodness to this page. But before we do, lets first prepare our uploads controller to handle uploaded files.

uploads_controller.rb

Loading Gist

Now replace the contents of our uploads new view with our upload form. Take note of the ' dropzone' class in our form. All we need to do is assign css class "dropzone" to the form. By default, DropzoneJS will find all forms with class "dropzone" and automatically attach itself to it and creating a file input field and upload files dropped into it to the specified action attribute.

new.html.erb

Loading Gist

We would however like file uploads to work even without JavaScript, to do that we include an element with the class fallback that dropzone will remove if the browser is supported. If the browser isn't supported, Dropzone will not create fallback elements if there is a fallback element already provided.

Next, I will add html mark up to work with bootstrap css to give our application a more appealing feel.

application.html.erb

Loading Gist

With that done, visiting http://0.0.0.0:3000/ should give you a good looking page like the one showed below

You may try dragging some image files but you will encounter an error. This is because the name of the file param that gets transferred defaults to file which is not what our application expects. We need to override this to match what our application expects. Rename uploads.js.coffee in (app/assets/javascripts) to uploads.js as we will be using pure javascript. Next paste in the following code to customize dropzone.

upload.js

Loading Gist

I've use the jQuery plugin Dropzone ships with though you can use the library without jquery see docs. This is just a tip of the iceberg when it comes to customizing dropzone. Head over to its full documentation and see ways on how to tweak it to suite your needs.

At this stage, we can now try draggin a couple of files on the upload area or by clicking our dropezone and wallah!! if everything worked out all your images should be successfully uploaded like below

Inspecting your application console should also give you something similar to mine

update (20/07/2014)

Since writing this tutorial, I recieved alot of requests on how to handle deleting of files from the server when the remove button is clicked. This is actually note very difficult. We need a way to uniquely identify the file to be deleted. To do this, when our uploaded file is successfully uploaded we will return the id of the uploaded file to dropezone and we will use this later when deleting the file. Also as you have guessed we also need a destroy action to our uploads controller.

uploads_controller.rb

Loading Gist

This destroy action is fairly standard, since we don't need any redirection, we just render a json response with a message that the file was successfully deleted.

Next up, we need to edit our uploads.js file. To do this, dropzonejs has a success function that fires when the file has been uploaded successfully. We tap into this function and after the file is successfully uploaded we will give our "remove file" button the id of the uploaded file we earlier returned in our controller.

Dropezonejs has another method removedfile which gets called whenever a file is removed from the list. We will listen to this and delete the file from our server through a DELETE ajax call.

uploads.js

Loading Gist

Hopefully this simple tutorial helped you with your development. If you have questions, do leave a comment below to let me know. Thanks for reading! ;-)


17 Comments

___

36f272c91db1e2ae44c35fa62b0d4ee70439353b

omugno

15 Jul 14

Hi men, i try to do this tutorial with rails 3.2.17 and paperclip 4.2, and i has a problem when i try to send de image paperclip show a error say that undefined method `upload_url', i think that maybe the error is becouse dropzone send the image in hexa and not in url, so maybe you can help please what is the error

Df7ddee1d56a7e5a4d852efa506d5098059a35a3

newterminator

21 Jul 14

Hey Joseph, I wanted to mention first of all that your implementation for the ajax multiple file upload with dropzone for rails 4 was amazing and it just worked perfectly. I spent days searching for a solution like yours. I wanted to ask you that in your blog and in the sample app, the delete part or "remove file" was not implemented, so if you could kindly update the code to reflect that as well, it would be great.

Ce337658a50868245f09ae8bdd858beee36cdff5

Rishi Ghan

27 Jul 14

Hi Joseph, thanks for the awesome tutorial! I almost got it working with polymorphic associations on my Attachment model. However, I have a problem where each image gets uploaded into its own post. I explained it here: http://stackoverflow.com/qu... Would really appreciate if you could point out what I am doing wrong. Thanks again!

Df7ddee1d56a7e5a4d852efa506d5098059a35a3

newterminator

07 Aug 14

Thanks Joseph for adding the delete feature, I would give it a shot and get back to you.

27329c0c78584b60c417cc1f2ffb43057a45fddb

Chris Mitchell

15 Aug 14

This was a vey helpful tutorial thanks very much. Could you provide some insight on how to work with multiple drop zone areas in one form?

Df7ddee1d56a7e5a4d852efa506d5098059a35a3

newterminator

30 Oct 14

Hey Joseph, Thank you once again for updating the code and the tutorial with the delete functionality. I downloaded the source code from github and ran the app, I noticed in the terminal that the images do get deleted from the system when the delete button is clicked, but the images on the browser page itself don't disappear. Finally when all the images are deleted the background image changes to show the "Drop files to upload or click" text but the images still remain on the foreground. On your heroku instance I noticed that the images disappear when delete is clicked upon; but on deletion of all the images the background in the Heroku version does not change to show the text - "Drop files to upload or click". Just wanted to ask you if you were aware of the issue and if there was a fix for it.

Df7ddee1d56a7e5a4d852efa506d5098059a35a3

newterminator

21 Nov 14

Hey Tim, I added your line but still the issue persists. In my terminal I get the following message. Started DELETE "/uploads/undefined" for 127.0.0.1 at 2014-11-20 20:47:21 -0500 Processing by UploadsController#destroy as */* Parameters: {"id"=>"undefined"} User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = 6 ORDER BY "users"."id" ASC LIMIT 1 Upload Load (0.1ms) SELECT "uploads".* FROM "uploads" WHERE "uploads"."id" = ? LIMIT 1 [["id", 0]] Completed 404 Not Found in 3ms ActiveRecord::RecordNotFound (Couldn't find Upload with 'id'=undefined): app/controllers/uploads_controller.rb:19:in `destroy'

F9cbcbd3db3408d952aefbf1a589003c810cb150

casertap

23 Nov 14

Hi JOSEPH, Great tutorial, thank you. I'm using this code right know to upload image on my server. What I also want is to have an index page that list all the picture already saved in the server inside the dropzone (so I can add or remove pictures later). Can you help me with that please? I found no solution.

0eb1169acb3635d6c09e9b57b5fc322480c36c9c

Samuel Ralak

27 Nov 14

Hey Joseph, do you know how to do this with a nested form? I can't figure out how to use this in a nested form. Any form of guidance will be highly appreciated

3eeda7827bc78bdba4d0db6853abd37415ac9adf

Riliwan Rabo Balogun

05 Mar 15

Hey Nice tuts but am trying to call dropzone programatically( not on the form). All the tuts i've seen is on the form , this included. However i get an error saying no route mathes POST 'rmployee/new' Below is my code

$("div#media-dropzone").dropzone({ url: "", paramName: "employee[avatar]", addRemoveLinks: true, dictDefaultMessage: 'upload' });

F384f25a77140412c19fd859bfb98e53af1c068d

Julian Ailan

23 Aug 15

Hi Joseph, great tutorial! The upload works great, the only problem I had is that the dropzone only renders when I refresh the webpage. The details of the problem and the code is in SO: http://stackoverflow.com/qu... . Wish you can tell what I'm missing. Thanks again!

F384f25a77140412c19fd859bfb98e53af1c068d

Julian Ailan

23 Aug 15

Thanks for that Joseph!

D62175522eb12153916477462abb3c05886b0bbd

frank0ch0a

24 Aug 15

Hi ! thanks for sharing , I have a question how can I use dropzonejs within a form with multiples fields?

3b65d168b97a8afc246d6a47c41f22d3139afe1b

vikram-hermes

03 Sep 15

Thanks for tutorials !! I tried to use it but facing a problem to upload files in different folders. I have 3 sections on a page 1. doc file - files should be uploaded to root/files/uploads/docs/ folder 2. images - uploaded to to root/files/uploads/docs/ images 3. compressed - uploaded to root/files/uploads/docs/ compressed is there a method to upload files to different directory, I am new to ruby on rails so dont know much on how paperclip works.

3ef7f4b49ee21b8eef0b6e623b9ce46a0a471553

ivan-vilches

19 Nov 15

Thanks for share this tutorial, any way to display the images example. using a index controller?

Dc605d2466d1b8dd9112c0f5e85ff549d1c60aea

vishaltps

25 Jan 16

where can i find this 2 files,.. dropzone.min.js dropzone.css i can't get a proper output and i also cant drag and drop images

E59386883e3c635c6d98e64986104cdcac42195b

praWINK

15 Nov 16

Is this possible to use this awesome tutorial in rails 5 with carrierwave

Latest Tutorials

___

Private Inbox System in Rails with Mailboxer New

Introduction It's been quite a while since my last tutorial and since then I've recieved alot of requests by email to implement a private messaging system ...

Ajax Sortable Lists Rails 4

With me, is a simple to-do list application where users can create dummy to-do lists and displays them in card-like form just like in Trello. We want to e...

Managing ENV variables in Rails

Often when developing Rails applications, you will find a need to setup a couple of environment variables to store secure information such as passwords, a...

Gmail Like Chat Application in Ruby on Rails

Introduction We are all fond of the Gmail and Facebook inline chat modules. About a week ago, I came across a tutorial on how to replicate hangouts chat...

Fast Autocomplete Search Terms - Rails

Introduction In many cases you find that you need to introduce a global search in your rails application i.e. search multiple models at a go using one form...

Load more scroll top