React Dropzone Node.js

Sometimes you may need to provide a page where users can upload files. It's quite easy if you only use HTML file input. However if you use a UI framework, you need different way to handle it. In addition, you may also want the upload page to support drag and drop functionality, so that users can select the files to be uploaded first on file explorer, drag the files and then drop them on the provided drop zone box. If you use ReactJS as the UI framework and you need to handle multiple files upload, you come to the right place. This tutorial shows you how to upload files, either single or multiple, using ReactJS and Node.js as the backend. We use ajax for sending the request to the server.

In this tutorial, I'll only focus on how to handle the request containing uploaded files in backend and how to create the React components as well as sending the request to the server. I assume you've already familiar with routing in Node.js, using template engine (such as Handlebars, pug (Jade), etc.), and of course the basic of ReactJS as well as how to compile your ReactJS code. To make it simple, I don't use Redux in this tutorial. But if you have been experienced with Redux and need to use it, it's quite easy to convert this non-Redux tutorial to Redux style.

Dependencies

Here is the dependenies we need for this tutorial

  "multer": "~1.3.1"
  "react": "~16.4.1",
  "react-dom": "~16.4.1",
  "react-dropzone": "~4.2.12"

You need to install those dependencies first.

Backend Code

Your application must be able to handle the request containing the uploaded files. The easiest way is using multer used as a middleware on the route(s) where you want to handle image upload. Here is the basic example of how to use multer as middleware.

src/routes/index.js

  const multer = require('multer');

  const fileUploadHandler = require('../src/file-upload/controllers/handlers/upload');

  const upload = multer({ dest: '/tmp' });

  router.post(
    '/file-upload',
    upload.any(),
    fileUploadHandler,
  );

The multer constructor supports the following key:

Key Description
dest or storage The directory where the uploaded files will be stored
fileFilter A function for controlling which files are accepted
limits Limit the uploaded files
preservePath Use full path of files instead of base name only

On the code above, we use upload.any(). The available options are:

Key Description
.single(fieldname) Accept a single file
.array(fieldname[, maxCount]) Accept array of files and you can set the maximum number of files
.fields(fields) Accept files with specified fields, you can set maxCount for each fields. Example:[ { name: 'mainphoto', maxCount: 1 }, { name: 'otherphotos', maxCount: 10 } ]
.none() Accept only text fields
.any() Accept all files

Here is the upload handler

src/file-upload/controllers/handlers/upload.js

  module.exports = (req, res) => {
    for (let i = 0; i < req.files.length; i += 1) {
      console.log(`File ${req.files[i].originalname} uploaded to ${req.files[i].path}`);
    }
  
    return res.status(200).send({ success: true });
  };

Frontend Code

Instead of making components from scratch, we're going to use react-dropzone module. It's a quite popular library, very easy to use and most importantly, it works. Using react-dropzone is quite easy. You only need to import and create a new component of it inside render() function. It accepts the following props:

Props Type Default Description
disableClick bool false If true, clicking on the dropzone won't open a file dialog
disabled bool false If true, the dropzone will be disabled
disablePreview bool false If true, preview generation will be disabled
preventDropOnDocument bool true If false, allow dropped items to take over the current browser window
inputProps object   Pass additional attributes to the <input type="file" /> tag
multiple bool true Allow dropping multiple files
name string   name attribute for the input tag
maxSize number Infinity Maximum file size (in bytes)
minSize number 0 Minimum file size (in bytes)
activeClassName string   Applied className when drag is active
acceptClassName string   Applied className when drop will be accepted
rejectClassName string   Applied className when drop will be rejected
disabledClassName string   Applied className when dropzone is disabled
style object   CSS styles to apply
activeStyle object   Applied CSS styles when drag is active
acceptStyle object   Applied CSS styles when drop will be accepted
rejectStyle object   Applied CSS styles when drop will be rejected
disabledStyle object   Applied CSS styles when dropzone is disabled
onClick() func   onClick callback. Arguments event: Event
onDrop(acceptedFiles, rejectedFiles) func   Will be triggered when on drop events
onDropAccepted(acceptedFiles) func   Will be triggered when on drop accepted events
onDropRejected(rejectedFiles) func    Will be triggered when on drop rejected events
onDragStart() func   Will be triggered when on drag start events
onDragEnter() func   Will be triggered when on drag enter events
onDragOver() func   Will be triggered when on drag over events
onDragLeave() func   Will be triggered when on drag leave events
onFileDialogCancel() func   Will be triggered when the user closes file dialog

And here is the basic example:

src/file-upload/views/containers/FileUploadContainer.jsx

  import Dropzone from 'react-dropzone';
  import React, { Component } from 'react';
  
  class FileUploadContainer extends Component {
    constructor() {
      super();
  
      this.onImageDrop = this.onImageDrop.bind(this);
    }
  
    onImageDrop(acceptedFiles) {
      const data = new FormData();
for (let i = 0; i < acceptedFiles.length; i += 1) { data.append('file', acceptedFiles[i]); } $.ajax({ url: '/file-upload', data, processData: false, contentType: false, method: 'POST', dataType: 'json', success: (response) => { if (response.success) { alert('success'); } else { alert('failed'); } }, error: (jqXHR) => { const res = jqXHR.responseJSON; alert('error: ' + JSON.stringify(res)); }, }); } render() { return ( <div> <Dropzone multiple onDrop={this.onImageDrop} > <p>Drop images or click to select a file to upload</p> </Dropzone> </div> ); } } export default FileUploadContainer;

That's all about how to upload multiple files using React.js and Node.js, by utilizing react-dropzone and multer.