File Handling with Multer

Introduction

My name is Nwobodo Chukwuebuka, and I am a beginner in web development. I started learning web development last year but haven't been consistent. I am a good learner, eager to gain knowledge and make an impact. I am passionate about what I do and always put in my best effort. However, I struggle with dedication and focus when no one is supervising me. That's why I registered for HNG11, as I want to complete my web development course and need challenges, guidance, and mentorship to do so.

I hope that by the end of the HNG class, I'll be a good web developer and data analyst, even though I currently have little knowledge of data analysis.

To improve my backend development skills, I built a profile generator that allows people to create a beautiful profile by entering their public name, preferred username, and a public bio of up to 150 words.

However, I encountered a roadblock when it came to handling the files that needed to be uploaded.

The challenge

I wondered how to stop people from uploading large files or files that aren't images. I needed to check that the uploaded image is really a picture and that it doesn't go over 2MB. This helps keep storage costs low as the product grows.

My Solution

Initially, I used to upload files without any checks, but considering the challenge mentioned above, I needed a solution. I did some research and discovered a package called Multer, which is a Node.js middleware for handling multipart/form-data, primarily used for uploading files.

Setup

First I installed multer.

npm install express multer

Import Multer

Then I import multer into my code

const multer = require('multer');

Setup the function that implements multer

Then I setup limits on the files size and filter the files to ensure it is an image and a single picture using multer.

const validateAndUpload = multer({
  limits: { fileSize: 2 * 1024 * 1024 }, // 2MB
  fileFilter: (req, file, cb) => {
    if (file.mimetype.startsWith('image/')) {
      cb(null, true);
    } else {
      cb(new Error('Only image files are allowed!'), false);
    }
  }
}).single('picture');

Application

Implementing this within my endpoint

app.post('/create-profile', validateAndUpload, (req, res) => {
  const { publicName, preferredUsername, publicBio } = req.body;

  // as you see from above by calling this post method, we also employ the
  //validateAndUpload function that we created initially which returns a true 
  //if what we received from the user is what we are looking for
  if (!req.file) {
    return res.status(400).json({ error: 'Picture is required and must be an image less than 2MB' });
  }

//this is what handles the response when the profle s creed successfully
  res.status(200).json({
    message: 'Profile created successfully',
    profile: {
      publicName,
      preferredUsername,
      publicBio,
      picture: req.file.filename
    }
  });
});

// This is meant to hadle whatever errors that come up while uploading
//it is more like a checker before any other thing is done.
app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    return res.status(400).json({ error: err.message });
  }
  if (err) {
    return res.status(500).json({ error: err.message });
  }
  next();
});

This way when someone uploads an image I can check the file type and the image size as well.

Finally

This is how the code looks at the end.

const express = require('express');
const multer = require('multer');
const app = express();
const port = 3000;

const validateAndUpload  = multer({
  limits: { fileSize: 2 * 1024 * 1024 },
  fileFilter: (req, file, cb) => {
    if (file.mimetype.startsWith('image/')) {
      cb(null, true);
    } else {
      cb(new Error('Only image files are allowed!'), false);
    }
  }
}).single('picture');

app.use(express.json());

app.post('/create-profile', validateAndUpload , (req, res) => {
  const { publicName, preferredUsername, publicBio } = req.body;

  if (!req.file) {
    return res.status(400).json({ error: 'Picture is required and must be an image less than 2MB' });
  }

  res.status(200).json({
    message: 'Profile created successfully',
    profile: {
      publicName,
      preferredUsername,
      publicBio,
      picture: req.file.filename
    }
  });
});

app.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    return res.status(400).json({ error: err.message });
  }
  if (err) {
    return res.status(500).json({ error: err.message });
  }
  next();
});

app.listen(port, () => {
  console.log(`Server is running on http://localhost:${port}`);
});

References

Below are useful links

Learn about the multer package

Expressjs

Thanks for your time. Hope you enjoyed it. Feel free to comment and join my newsletter to keep yourself up-to-date with future release.

https://hng.tech/internship

https://hng.tech/premium

~OracleJnr

0
Subscribe to my newsletter

Read articles from chukwuebua Emmanuel Nwoboodo directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

chukwuebua Emmanuel Nwoboodo
chukwuebua Emmanuel Nwoboodo