How to upload files using multer in NodeJS

nodejs multer image

File uploading is an essential feature for most of the web applications nowadays. Today we are going to learn how to upload files to a server using a third party package in NodeJS called Multer. Multer is a very popular package to handle file uploading in NodeJS. You can download project files at the end of the tutorial.

Creating a project.

Let’s first create a fresh project using npm to start learning Multer. Create a directory for our project in my case I am going to name it multer-example. Now open your terminal in this directory. We need to initialize this directory first to use this directory as our NodeJS project. In your terminal type npm init -y. This command npm init will initialize our current directory with a package.json file which is used for various configurations of our NodeJS project and -y parameter set the defaults value to the package.json file. you can see it in the below picture.

multer-installation-command

Installing all the dependencies.

Now we need to install all the dependencies for our project. I will be using ExpressJS so that I don’t have to write all the server logic from scratch and for template parsing, I’m going to use the ejs template engine. Now in your terminal type the following command.

npm install –save express multer ejs

This command will install expressjs, multer, and ejs for our project in node_modules folder. After that our package.json file will look something like the following

{
  "name": "multer-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "nodemon app.js",
    "test": "echo \"Error: no test specified\""
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ejs": "^2.6.1",
    "express": "^4.16.4",
    "multer": "^1.4.1"
  }
}

Defining our project structure

Our main code will be in the app.js file which will be stored in our root folder. After that, we need to create a public and views folder. The public folder will store all the assets which will be publicly exposed to use in our app. Public folder will contain CSS, javascript, images, and other static files which we will require in our app. Inside the public folder will need to create two more folders named css and uploads. The css folder will hold our .css file which we will be using in our app and uploads folder will store the all the files which we will upload through our web app. In the end, our project structure will look like the below picture.

multer demo project structure example

Let’s dive into creating our web app to upload files.

Create the app.js file in the root folder. The first thing to do in our code is to import all the modules which we are going to need while creating our web app.

const express = require('express');
const multer = require('multer');
const ejs = require('ejs');
const path = require('path');

First of all, we need to initialize our express and make our server to listen for request on port number 3000

const port = 3000

// Init app
const app = express()

app.listen(port, () => console.log('Server started at port : ' + port))

After that go to your terminal and type node app.js  and it will start our server and you can see the statement in the terminal that we have written our code. But you go to the //localhost:3000, you will get an error like shown below.

server error

We are getting this error because we have not set any route to the ‘/’ which is our default route.

We need to make our app to listen for the default route so that we can get rid of the error. But before setting up the default route we need to set our view engine which will be used to render our HTML files to our browser and we also need to set up our public folder which we have created earlier to set as static so that we can access our static files.

// Set view engine
app.set('view engine', 'ejs')

// Set static folder
app.use(express.static('./public'));

Now we are ready to set our route for the homepage. Before that, we need to create index.ejs file in the views folder. I am going to use Bootstrap 4 for our front-end design.

index.ejs

<!doctype html>
<html lang="en">

<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="//stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO"
        crossorigin="anonymous">

    <link rel="stylesheet" href="/css/index.css?x26402">

    <title>Multer Demo</title>
</head>

<body>
    <div class="container">
        <div class="row justify-content-center">
            <!-- <div class="col"></div> -->

            <div class="col-xs">
                <h1 class="text-center mt-5">Upload Files</h1>
                <form method="POST" action="/upload" enctype="multipart/form-data">
                    <div class="text-center">
                        <%= typeof msg != 'undefined' ? msg : '' %>
                    </div>
                    <div class="form-group upload-area">
                        <label for="exampleFormControlFile1">Click Here To upload Files</label>
                        <input type="file" name="files" class="form-control-file" id="exampleFormControlFile1">
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-primary">Upload</button>
                    </div>
                </form>
            </div>
            <!-- <div class="col"></div> -->
        </div>

    </div>
    <script src="//stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
        crossorigin="anonymous"></script>
</body>

</html>

index.css

We will keep out css file inside public>css folder

*{
    margin: 0;
    padding: 0;
}

body{
    background-color: bisque
}

.upload-area{
    border: 2px dotted blue;
    padding: 40px;
    background: lightblue;
    margin: 26px 200px;
    text-align: center;
    font-size: 25px;
    font-weight: lighter;
}

input[type="file"]{
    display: none;
}

button{
    width: 47%;
    margin-left: 198px;
}

After that our app.js file will look like this.

app.js

const express = require('express');
const multer = require('multer');
const ejs = require('ejs');
const path = require('path');

const port = 3000;

// Init app
const app = express()

// Set view engine
app.set('view engine', 'ejs')

// Set static folder
app.use(express.static('./public'));

// Set the initial route
app.get('/', (req, res) => {
    res.render('index');
})

app.listen(port, () => console.log('Server started at port : ' + port))

Now if go to the //localhost:3000/ final output will look like this.

multer-demo-final-frontend

Now we ready to use multer to upload files on our web server.

Setting up the Multer

First, we need to tell the Multer how to are going to store our files. There are two ways to store the files in multer.

  1. Storing the files on our local disk using the diskStorage method. The disk storage engine gives you full control of storing files to disk.
  2. Storing the files in the memory as Buffer objects.

In our example, we will use the diskStorage method to store our files on our local hard disk. The diskStorage method needs to have to options to be passed to it. First is the destination and second is the filename . Both options will determine how we are going to store our files.

Write the below code after you have initialized the expressjs.

// Set storage engine
const storage = multer.diskStorage({
    destination: './public/uploads',
    filename: function (req, file, cb) {        
        // null as first argument means no error
        cb(null, Date.now() + '-' + file.originalname )
    }
})

Here destination field specifies where do we actually want to store our uploaded files, in this case, we would like to store our uploaded files in the uploads directory we have created previously and filename field takes a function which will have three arguments. The req argument handles our incoming request, The file arguments contain information related to the uploaded, If you log the file argument on the console you will find the following details.

{ 
  fieldname: 'files',
  originalname: 'image.png',
  encoding: '7bit',
  mimetype: 'image/png' 
}

The third argument cb  is a callback which will write the file to our disk and we have passed two parameters to it, The first parameter is null which means we do not have any errors and second is the filename which will be used as name for the file while storing the file on our disk, Here we actually want to rename it to something different than the original file because if we upload the same file twice than it will throw an error saying that file already exists and we do not want that to happen so we are prepending the timestamp so that we get a unique file name every time we upload an image file. We have completed the setup for the storage and now Multer knows where and how it can store the file which we want to upload on our web app.

Setting up the last piece of the puzzle.

We have configured, how and where our file is going to store. Now need to configure what files and how to upload them. We need to define an upload method which will handle the file related validations such as mime type validations, extension validations, file size validation etc. In this method, we will initialize the multer so that it can be ready to handle file uploads.

// Init upload
const upload = multer({
    storage: storage, 
    limits: {
        fileSize: 1000000
    },
    fileFilter: function (req, file, cb) {
        sanitizeFile(file, cb);
    }
}).single('files')

In the multer method, we can pass the following key/pair values to configure the file upload as per our need.

KeyDescription
dest or storageWhere to store the files
fileFilterFunction to control which files are accepted
limitsLimits of the uploaded data
preservePathKeep the full path of files instead of just the base name

For the storage key, we have assigned it the storage constant we have already created and next key is limits which takes a JSON configuration for the file size and the amount is specified in bytes, Here we have set it to 1 M. For the function of the fileFilter key, Let’s define a custom function called sanitizeFile which will handle our file validation code.

function sanitizeFile(file, cb) {
    // Define the allowed extension
    let fileExts = ['png', 'jpg', 'jpeg', 'gif']

    // Check allowed extensions
    let isAllowedExt = fileExts.includes(file.originalname.split('.')[1].toLowerCase());
    // Mime type must be an image
    let isAllowedMimeType = file.mimetype.startsWith("image/")

    if(isAllowedExt && isAllowedMimeType){
        return cb(null ,true) // no errors
    }
    else{
        // pass error msg to callback, which can be displaye in frontend
        cb('Error: File type not allowed!')
    }
}

At the end of the upload  method we have called another method on upload which is single(‘files’) this method specifies , how many files we want to upload at once and it takes one parameter which is input tag name in out HTML code, In our case it is files. Multer provides similar methods  like single().Let’s take a look at the following methods

  1. single(fieldname) – Accept a single file with the name fieldname. The single file will be stored
  2. array(fieldname[, maxCount]) – Accept an array of files, all with the name fieldname. Optionally error out if more than maxCount files are uploaded. The array of files will be stored in req.files.
  3. fields(fields) – Accept a mix of files, specified by fields. An object with arrays of files will be stored in req.files.

The complete code will look like following,

const express = require('express');
const multer = require('multer');
const ejs = require('ejs');
const path = require('path');

const port = 3000;

// Init app
const app = express()


// Set storage engine
const storage = multer.diskStorage({
    destination: './public/uploads',
    filename: function (req, file, cb) {        
        // null as first argument means no error
        cb(null, Date.now() + '-' + file.originalname )
    }
})

// Init upload
const upload = multer({
    storage: storage, 
    limits: {
        fileSize: 1000000
    },

    fileFilter: function (req, file, cb) {
        sanitizeFile(file, cb);
    }

}).single('files')



// Set view engine
app.set('view engine', 'ejs')

// Set static folder
app.use(express.static('./public'));


// Set the initial route
app.get('/', (req, res) => {
    res.render('index');
})


// Handle the upload route
app.post('/upload', (req, res) => {
    // res.send('done');
    upload(req, res, (err) => {
        if (err){ 
            res.render('index', { msg: err})
        }else{
            // If file is not selected
            if (req.file == undefined) {
                res.render('index', { msg: 'No file selected!' })
            
            }
            else{
                res.render('index', { msg: 'File uploaded successfully!' })
            }

        }
    
    })
})

function sanitizeFile(file, cb) {
    // Define the allowed extension
    let fileExts = ['png', 'jpg', 'jpeg', 'gif']

    // Check allowed extensions
    let isAllowedExt = fileExts.includes(file.originalname.split('.')[1].toLowerCase());
    // Mime type must be an image
    let isAllowedMimeType = file.mimetype.startsWith("image/")

    if (isAllowedExt && isAllowedMimeType) {
        return cb(null, true) // no errors
    }
    else {
        // pass error msg to callback, which can be displaye in frontend
        cb('Error: File type not allowed!')
    }
}

app.listen(port, () => console.log('Server started at port : ' + port))

Now we can upload image files to our server using multer. You can customize the code to upload any type of files you want using Multer.

If you have any suggestions or issues please write it in the comment box….

Happy Coding… 🙂

Download Project From GitHub
Ropali Munshi
Ropali Munshi
Ropali Munshi is fullstack PHP Developer. He is passionate developer who loves to learn and expirement with new programming languages , libraries and frameworks. Nowdays he is more into the JavaScript realm.

2 Comments

  1. test says:

    How to uploaded in front-end

Leave a Reply

Your email address will not be published. Required fields are marked *