How to Upload Files using the MERN stack and Multer.

ยท

7 min read

Blog-Article-MERN-Stack.jpg File uploads is an essential part of software development. It has revolutionized the way we work, socialize and learn as humans. From building social media apps, to video streaming apps, to online learning platforms. The list goes on and on.

File upload is applied (but not limited to) the following areas:

  • picture uploads (as seen in social media platforms)
  • multimedia uploads

Objective

By the end of this article, you'll be able to implement file uploads using the MERN stack and multer. A big step in building the next big thing.

Prerequisites

To follow through this tutorial, I assume you have basic knowledge of the following technologies:

  • mongoDB
  • express.js
  • react.js
  • react hooks
  • node.js

Folder structure

Our application will have a backend and a frontend. The structure of your app is entirely up to you!

Frontend

In the frontend, create a simple form. The form is to have encType attribute set to "multipart/form-data". Here's a sample code.

import React, {useState} from 'react';

function App () {
  const [uploadedFile, setUploadedFile] = useState ('');
  const [fileTitle, setFileTitle] = useState ('');

  function handleFormSubmittion (e) {
    e.preventDefault ();

    let form = document.getElementById ('form');
    let formData = new FormData (form);

    // do something
    console.log("Form submitted")
  }

  function handleFileTitle (e) {
    setFileTitle (e.target.value);
  }

  function handleUploadedFile (e) {
    setUploadedFile (e.target.value);
  }

  return (
    <React.Fragment>
      <h1>File upload</h1>
      <form
        encType="multipart/form-data"
        onSubmit={handleFormSubmittion}
        id="form"
      >
        <input
          type="file"
          name="uploadedFile"
          value={uploadedFile}
          onChange={handleUploadedFile}
          required
        />
        <br />
        <br />

        <label>File title:</label><br />
        <input
          type="text"
          placeholder="Enter file title"
          name="fileTitle"
          value={fileTitle}
          onChange={handleFileTitle}
          required
        />
        <br />
        <br />

        <button type="submit">Submit Form</button>
      </form>
    </React.Fragment>
  );
}

export default App;

Backend

To get setup the backend, install the following dependencies:

  • express
  • cors
  • body-parser
  • multer

Multer is responsible for handling multipart forms. It adds a body object and a file or files object to the request object. The body object contains the values of the text fields of the form, the file or files object contains the files uploaded via the form.

To get started, we create a storage engine. The storage engine is responsible for the following:

  • file destination
  • file renaming

There are two options available: destination and filename. They are both functions that determine where the file should be stored. Destination is used to determine where the uploaded files should be stored. This can also be given as a string (e.g. '/tmp/uploads'). If no destination is given, the operating system's default directory for temporary files is used. Filename is used to determine what the file should be named inside the folder. If no filename is given, each file will be given a random name that doesn't include any file extension.

Here's the code

// storage engine for multer
const storageEngine = multer.diskStorage ({
  destination: './public/uploads/',
  filename: function (req, file, callback) {
    callback (
      null,
      file.fieldname + '-' + Date.now () + path.extname (file.originalname)
    );
  },
});

Next, we create a file filter (optional).It lets us filter out unwanted files. For this tutorial, we will expect only images.

Here's the code.

// file filter for multer
const fileFilter = (req, file, callback) => {
  let pattern = /jpg|png|svg/; // reqex

  if (pattern.test (path.extname (file.originalname))) {
    callback (null, true);
  } else {
    callback ('Error: not a valid file');
  }
};

Then, we initialize multer.

Here's the code.

// initialize multer
const upload = multer ({
  storage: storageEngine,
  fileFilter  
});

Finally, we create the routes for our application.

Multer. provides a single method for acessing single files. It takes a string as an argument.

NOTE: It is important that you use the name field value from the form in your upload function. This tells multer which field on the request it should look for the files in. If these fields aren't the same in the HTML form and on your server, your upload will fail.

Here's the code:

// routing
app.post ('/upload', upload.single ('uploadedFile'), (req, res) => {
  console.log(req.file)
  res.json (req.file).status (200);
});

Our final backend code should look like this:

const express = require ('express');
const bodyParser = require ('body-parser');
const cors = require ('cors');
const path = require ('path');
const multer = require ('multer');

// constants
const app = express ();
const PORT = process.env.PORT || 5000;

// middleware
app.use (bodyParser.json ());
app.use (bodyParser.urlencoded ({extended: true}));
app.use (cors ());

// storage engine for multer
const storageEngine = multer.diskStorage ({
  destination: './public/uploads/',
  filename: function (req, file, callback) {
    callback (
      null,
      file.fieldname + '-' + Date.now () + path.extname (file.originalname)
    );
  },
});

  // file filter for multer
  const fileFilter = (req, file, callback) => {
    let pattern = /jpg|png|svg/; // reqex

    if (pattern.test (path.extname (file.originalname))) {
      callback (null, true);
    } else {
      callback ('Error: not a valid file');
    }
  };

// initialize multer
const upload = multer ({
  storage: storageEngine,
  fileFilter: fileFilter,
});

// routing
app.post ('/upload', upload.single ('uploadedFile'), (req, res) => {
  res.json (req.file).status (200);
});

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

Connecting your Frontend to your Backend

To connect your frontend and your backend, do the following:

  1. install axios
  2. make a post request to the backend

Here's what your handleFormSubmittion method should look like.

  function handleFormSubmittion (e) {
    e.preventDefault ();

    let form = document.getElementById ('form');
    let formData = new FormData (form);

    // new line added
    axios.post ('http://localhost:5000/upload', formData);
  }

To confirm your app is working correctly, upload a picture. All uploads will be stored in backend/public/uploads.

Conclusion

File uploads are essential when building modern web apps. I tried my best to make this article as simple as possible. You can re-read this article if the concepts were not clear. Your feedbacks are essential. It helps me put out better content.

Stay safe:)