Build an HTML5 Image Uploader with DropzoneJS

By HTMLGoodies Staff

WEBINAR: On-demand webcast

How to Boost Database Development Productivity on Linux, Docker, and Kubernetes with Microsoft SQL Server 2017 REGISTER >

Build an HTML5 Image Uploader with DropzoneJS

Some time ago, I wrote a couple of tutorials on how to design and code a file upload control using the Blueimp jQuery-File-Upload Plugin. Both those articles, along with one on dragging files into the browser using jQuery event binding, generated the most interest of my thousand-plus pieces so far. Today I'm thrilled to announce that I've stumbled across a JavaScript library that combines both of these coveted features into one easy-to-use package. named DropzoneJS, it's an open source library that provides drag n' drop file uploads with image previews. It's not only lightweight, fully customizable, but it doesn't depend on any other library (like jQuery) either. They have a demo on their main page. In today's tutorial, we'll learn how to recreate it.

Referencing the Library

The DropzoneJS library consists of one .js and an optional .css file. Both are available for download from the Github repository. (There are some additional files for use with component and RequireJS as well.) You can either download the files from there or do what the cool kids are doing and link to a trusted host such as cdnjs.

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/4.3.0/dropzone.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/4.3.0/dropzone.css" />
</head>

The HTML Markup

The simplest way to use DropzoneJS is to create a form element with the class "dropzone". That's it; while our form has an extra DIV with instructions, the dropzoneJS library will create the clickable area for you as well as present a fallback upload control for older browsers.

<h1>DropzoneJS File Upload Demo</h1>
<SECTION>
  <DIV id="dropzone">
    <FORM class="dropzone needsclick" id="demo-upload" action="/upload">
      <DIV class="dz-message needsclick">   
        Drop files here or click to upload.<BR>
        <SPAN class="note needsclick">(This is just a demo dropzone. Selected
        files are <STRONG>not</STRONG> actually uploaded.)</SPAN>
      </DIV>
    </FORM>
  </DIV>
</SECTION>

Simulating the Server Component

Dropzone does not provide the server side implementation of handling the files, but it submits the files to the server exactly like a form with "multipart/form-data" encoding would.

Like the message says, the dropzone component reports success even though the files are not being uploaded. The appearance of uploading the files is achieved by the programmatic creation of the dropzone along with the inclusion of some duplicated HTML elements.

Let's start with the instantiation of the dropzone class. The Dropzone (upper case 'D') constructor takes the ID of the target component as well as an object of options. The latter can even be utilized to override methods:

// Dropzone class:
var dropzone = new Dropzone('#demo-upload', {
  previewTemplate: document.querySelector('#preview-template').innerHTML,
  parallelUploads: 2,
  thumbnailHeight: 120,
  thumbnailWidth: 120,
  maxFilesize: 3,
  filesizeBase: 1000,
  thumbnail: function(file, dataUrl) {
    if (file.previewElement) {
      file.previewElement.classList.remove("dz-file-preview");
      var images = file.previewElement.querySelectorAll("[data-dz-thumbnail]");
      for (var i = 0; i < images.length; i++) {
        var thumbnailElement = images[i];
        thumbnailElement.alt = file.name;
        thumbnailElement.src = dataUrl;
      }
      setTimeout(function() { file.previewElement.classList.add("dz-image-preview"); }, 1);
    }
  }

});

The next step is to fake the file upload, since the server returns a 404. That's done by overriding the uploadFiles() function. The resulting code is fairly involved, but you can see where it sets the file.status to Dropzone.SUCCESS. There's also a lot of work going into making the upload progress appear normal.

var minSteps = 6,
    maxSteps = 60,
    timeBetweenSteps = 100,
    bytesPerStep = 100000;

dropzone.uploadFiles = function(files) {
  var self = this;

  for (var i = 0; i < files.length; i++) {

    var file = files[i];
    totalSteps = Math.round(Math.min(maxSteps, Math.max(minSteps, file.size / bytesPerStep)));

    for (var step = 0; step < totalSteps; step++) {
      var duration = timeBetweenSteps * (step + 1);
      setTimeout(function(file, totalSteps, step) {
        return function() {
          file.upload = {
            progress: 100 * (step + 1) / totalSteps,
            total: file.size,
            bytesSent: (step + 1) * file.size / totalSteps
          };

          self.emit('uploadprogress', file, file.upload.progress, file.upload.bytesSent);
          if (file.upload.progress == 100) {
            file.status = Dropzone.SUCCESS;
            self.emit("success", file, 'success', null);
            self.emit("complete", file);
            self.processQueue();
          }
        };
      }(file, totalSteps, step), duration);
    }
  }
}

That brings us to the duplicated HTML elements. These include a whole wack of information for the thumbnails, messages, as well as the success checkmarks and error "X" graphic. Notice that the success and error checkmarks are rendered using SVG.

<DIV id="preview-template" style="display: none;">
<DIV class="dz-preview dz-file-preview">
<DIV class="dz-image"><IMG data-dz-thumbnail=""></DIV>
<DIV class="dz-details">
<DIV class="dz-size"><SPAN data-dz-size=""></SPAN></DIV>
<DIV class="dz-filename"><SPAN data-dz-name=""></SPAN></DIV></DIV>
<DIV class="dz-progress"><SPAN class="dz-upload"
data-dz-uploadprogress=""></SPAN></DIV>
<DIV class="dz-error-message"><SPAN data-dz-errormessage=""></SPAN></DIV>
<div class="dz-success-mark">
  <svg width="54px" height="54px" viewBox="0 0 54 54" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
    <!-- Generator: Sketch 3.2.1 (9971) - http://www.bohemiancoding.com/sketch -->
    <title>Check</title>
    <desc>Created with Sketch.</desc>
    <defs></defs>
    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
        <path d="M23.5,31.8431458 L17.5852419,25.9283877 C16.0248253,24.3679711 13.4910294,24.366835 11.9289322,25.9289322 C10.3700136,27.4878508 10.3665912,30.0234455 11.9283877,31.5852419 L20.4147581,40.0716123 C20.5133999,40.1702541 20.6159315,40.2626649 20.7218615,40.3488435 C22.2835669,41.8725651 24.794234,41.8626202 26.3461564,40.3106978 L43.3106978,23.3461564 C44.8771021,21.7797521 44.8758057,19.2483887 43.3137085,17.6862915 C41.7547899,16.1273729 39.2176035,16.1255422 37.6538436,17.6893022 L23.5,31.8431458 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z" id="Oval-2" stroke-opacity="0.198794158" stroke="#747474" fill-opacity="0.816519475" fill="#FFFFFF" sketch:type="MSShapeGroup"></path>
    </g>
  </svg>
</div>
<div class="dz-error-mark">
  <svg width="54px" height="54px" viewBox="0 0 54 54" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
      <!-- Generator: Sketch 3.2.1 (9971) - http://www.bohemiancoding.com/sketch -->
      <title>error</title>
      <desc>Created with Sketch.</desc>
      <defs></defs>
      <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
          <g id="Check-+-Oval-2" sketch:type="MSLayerGroup" stroke="#747474" stroke-opacity="0.198794158" fill="#FFFFFF" fill-opacity="0.816519475">
              <path d="M32.6568542,29 L38.3106978,23.3461564 C39.8771021,21.7797521 39.8758057,19.2483887 38.3137085,17.6862915 C36.7547899,16.1273729 34.2176035,16.1255422 32.6538436,17.6893022 L27,23.3431458 L21.3461564,17.6893022 C19.7823965,16.1255422 17.2452101,16.1273729 15.6862915,17.6862915 C14.1241943,19.2483887 14.1228979,21.7797521 15.6893022,23.3461564 L21.3431458,29 L15.6893022,34.6538436 C14.1228979,36.2202479 14.1241943,38.7516113 15.6862915,40.3137085 C17.2452101,41.8726271 19.7823965,41.8744578 21.3461564,40.3106978 L27,34.6568542 L32.6538436,40.3106978 C34.2176035,41.8744578 36.7547899,41.8726271 38.3137085,40.3137085 C39.8758057,38.7516113 39.8771021,36.2202479 38.3106978,34.6538436 L32.6568542,29 Z M27,53 C41.3594035,53 53,41.3594035 53,27 C53,12.6405965 41.3594035,1 27,1 C12.6405965,1 1,12.6405965 1,27 C1,41.3594035 12.6405965,53 27,53 Z" id="Oval-2" sketch:type="MSShapeGroup"></path>
          </g>
      </g>
  </svg>
</div>

As I've been keen to do these days, here is the working demo on Codepen.io.

Conclusion

DropzoneJS is a great library and I'm looking forward to playing with it over the coming months.



Rob Gravelle

Rob Gravelle resides in Ottawa, Canada, and has built web applications for numerous businesses and government agencies. Email him for a quote on your project.

Rob's alter-ego, "Blackjacques", is an accomplished guitar player, that has released several CDs. His band, Ivory Knight, was rated as one of Canada's top hard rock and metal groups by Brave Words magazine (issue #92) and reached the #1 spot in the National Heavy Metal charts on ReverbNation.com.



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •  
Thanks for your registration, follow us on our social networks to keep up-to-date