~/blog/file-upload-nodejs-using-expressjs-multer
Published on

File Upload in Node.js Using Express.js and Multer

book4 minutes read

Upload file node.js

source

Still related to my previous post on CRUD REST API in Node.js Using Express.js and MongoDB, in this post, I want to create a tutorial on how to handle image file uploads using Node.js with the Express.js Web Framework and Multer.

What is Multer? Multer is a middleware for handling multipart/form-data, which is commonly used for file upload functionality.

So, what is middleware? It's an intermediary between the router and the handler / controller. In short, when we make a request to a specific route or endpoint, middleware will perform a certain process first before proceeding to the handler. It's commonly used for tasks like authentication, logging, input validation, and many other processes.

Preparation

First, here's a list of tools I used while creating this tutorial:

node.js v15.0.1
npm v7.0.3
yarn v1.22.5

Alright... let's start by creating a new directory and initializing the package.json for the project.

mkdir nodejs-file-upload
cd nodejs-file-upload
yarn init -y

Kemudian install paket yang dibutuhkan

yarn add express multer cors

Create the Main File

Create a new file named index.js in the root directory of the project.

// ./index.js

const express = require('express');
const cors = require('cors');

const port = process.env.PORT || 1337;
const routes = require('./routes');

const app = express();

app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(routes);

app.listen(port, () => {
  console.log('The magic happen on port :' + port);
});

Create Routes

Create a directory named routes in the root directory of the project and create a new file index.js inside it.

// routes/index.js

const express = require('express');
const router = express.Router();
const multer = require('multer');
const storage = multer.diskStorage({
  // Set the directory where the uploaded images will be stored
  destination: 'public/',
  // Set the file name when the image is saved
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  }
});
const upload = multer({ storage: storage });
// Import the uploader handlernya
const handlerUploader = require('../handlers/uploader');

// Route that points to http://localhost:1337
router.get('/', async (req, res) => {
  return res.json({ message: 'Hello World!' });
});
// Route that points to http://localhost:1337/uploader
router.post('/uploader', upload.single('image'), handlerUploader.single);

module.exports = router;

Create Helper

Create a directory named helpers in the root directory of the project and create a new file named uploader.js inside it

// helpers/uploader.js

const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
// Function to generate a random string
exports.randomFilename = (length) => {
  return crypto
    .randomBytes(length)
    .toString("hex");
}

// Function to validate the file extension of the uploaded file
exports.getValidExtension = (filename) => {
  // List of file extensions
  const formatAllowed = ["jpg", "jpeg", "png"];

  // Get the extension of the uploaded file
  const ext = filename
    .substr(filename.lastIndexOf(".") + 1)
    .toLowerCase();

  // If the file extension matches one from the list
  if (formatAllowed.indexOf(ext) > -1) {
    // Return the string of the file extension
    return ext;
  }
  else {
    return false;
  }
}

// Function to move the uploaded file
exports.saveImage = (pathFrom, pathTo) => {
  try {
    // Check if the target directory exists
    if ( ! fs.existsSync(pathTo)) {
      // If not, create the folder first
      const destination = path.dirname(pathTo);
      fs.mkdirSync(destination, { recursive: true });
    }

    // Rename the uploaded file with the new name and save it in the target directory
    fs.renameSync(pathFrom, pathTo);
  }
  catch (error) {
    throw (message = error);
  }
};

Create Handler

Create a directory named handlers in the root directory of the project, and inside it, create a new file named uploader.js

// handlers/uploader.js

const fs = require('fs');
// Import the necessary functions from the uploader helper
const {
  getValidExtension,
  randomFilename,
  saveImage
} = require('../helpers/uploader');

exports.single = async (req, res) => {
  try {
    // If no file is uploaded, return an error message
    if ( ! req.file) {
      return res.status(422).json({ message: `File tidak ditemukan.` });
    }

    // Check the file extension
    const extension = await getValidExtension(req.file.originalname);
    // If the extension is not valid
    if ( ! extension) {
      // Delete the uploaded file
      fs.unlinkSync(req.file.path);
      // Return an error response
      return res.status(422).json({ message: `Format file tidak valid.` });
    }

    // Generate a new random file name
    const filename = await randomFilename(16);
    // Get the directory where the file is stored by multer
    const pathFrom = req.file.path;
    // Create the file storage directory if it doesn't exist
    const pathTo = `public/images/${filename}.${extension}`;
    // Save the image file
    saveImage(pathFrom, pathTo);

    const response = {
      message: 'Upload berhasil.',
      image_path: pathTo
    }

    return res.json(response);
  }
  catch (error) {
      console.log(error);
      return res.status(500).json(error);
  }
}

Run the Server

Finally, try running the server using node, pm2, or nodemon:

$ node index.js
The magic happens on port :1337

If you see this message, the server is up and running.

Testing

I’ve created a collection for Postman to facilitate your testing. You can download the collection here.

You can check the project repository on GitHub.

That’s all for now, see you~