DEV Community

Cover image for Upload Single File In Node.js Using Express and Multer in 6 Steps
Aimal Maarij
Aimal Maarij

Posted on • Edited on

Upload Single File In Node.js Using Express and Multer in 6 Steps

Every one of us takes different approaches while coding and building our folder and file structure for the projects. Here is the approach that I took for my first #Node application.

The front-end is build with #react-app.

Installation

// run this for yarn
yarn add express cors multer

// or using npm
npm install express cors multer --save
Enter fullscreen mode Exit fullscreen mode

Note:

• express: We will develop our API using ExpressJs
• cors: A node.js package that provides an Express/Connect middleware to enable Cross Origin Resource Sharing (CORS)
• multer: Node.js middleware for handling multipart/form-data

Project structure

This project is a nodejs application using express framwork.
Alt Text

  • The express application inside the index.js file has one API end-point call.
  • Routes.js includes an API end-Point call for uploading file and updating the user collection in the database.
  • UserModel.js is a mongodB model.

1. Index.js File

import express from 'express';
import bodyParser from 'body-parser';
import mongoose from 'mongoose';
import cors from 'cors';
import dotenv from 'dotenv';

import Routes from './routes.js';

const app = express();
dotenv.config();

app.use(express.static('./public'));
app.use('/uploads', express.static('uploads'));

app.use(bodyParser.json({ limit: '30mb', extended: true }))
app.use(bodyParser.urlencoded({ limit: '30mb', extended: true }))

app.use(cors());

app.use(/myapi,Routes);

const PORT = process.env.PORT|| 5000;

mongoose.connect(process.env.CONNECTION_URL, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => app.listen(PORT, () => console.log(`Server Running on Port: http://localhost:${PORT}`)))
  .catch((error) => console.log(`${error} did not connect`));

mongoose.set('useFindAndModify', false);
Enter fullscreen mode Exit fullscreen mode
Important notes:

app.use('/uploads', express.static('uploads'));

This middleware sets "upload" folder as static where the file will be uploaded in. Make sure to create a folder called upload in your application. This name can change to whatever you like to change.

app.use('/my_api', Routes);

This line of code is for API end-point call for the main route that is called my_api.

2. Route.js File

import express from 'express';
import { updateAnUserImage } from './userController.js';
import upload from './upload.js'

const router = express.Router();
router.patch('/user/:_id', upload, updateAnUserImage);

export default router;

Enter fullscreen mode Exit fullscreen mode
Important notes:

router.patch('/user/:_id', upload, updateAnUserImage);

The user route will be added after my_api which was the main route in the index.js file. This route should look like this: my_api/user/id.

The upload middleware allows the user to upload a file at this API end-point call. The "updateAnUserImage" is the controller that takes the path and the linked of the file and insert it to the database.

3. upload.js file (middleware)

import multer from 'multer';

const storage = multer.diskStorage({
    destination: function (req, file, cb) {
        cb(null, './uploads');
    },
    filename: function (req, file, cb) {
        cb(null, Date.now() + "--" + file.originalname);
    }
});  

const fileFilter = (req, file, cb) => {
    if((file.mimetype).includes('jpeg') || (file.mimetype).includes('png') || (file.mimetype).includes('jpg')){
        cb(null, true);
    } else{
        cb(null, false);

    }

};

let upload = multer({ storage: storage, fileFilter: fileFilter,});

export default upload.single('ProfilePicture')

Enter fullscreen mode Exit fullscreen mode
Important notes:

This is the place where Multer is used to define the file destination, file name, filteration and the logic for uploading the file.

The constant "storage" defines where the file will be uploaded and what will be the file name.

The fileFilter is function declaration that takes three parameters; "request, file and a call back". The logic for the filtration is as below:

if the file type is JPEG,JPG or PPNG then trigger the cb. The cb function takes two arguments first null and the second one is a Boolean. True means to allow save the file to the storage and false means to reject the file saving process to the storage.

4. userController.js (The controller file)

import User from './userModel.js';

export const updateAnUserImage = async (req, res) => {
    const id = req.params._id;

    if (!mongoose.Types.ObjectId.isValid(id)) return res.status(404).send(`No post with id: ${id}`);

    const path = req.file.path.replace(/\\/g, "/")

    await User.findByIdAndUpdate(id, req.body = {ProfilePicture: "http://localhost:5000/" + path}, { new: true });
    res.json(updateAnUser);
}

Enter fullscreen mode Exit fullscreen mode
Important notes:

The controller exports updateAnUserImage. updateAnUserImage is an asynchronous function that takes two parameters: request and response. This function checks if there is an _id in the params in request body. if yes then replace all the front slashes to the back slashes in the path string and then update the value of the ProfilePicture to the constant path. In simple words, it means to update the file link in the database.

5. UserModel.js file

import mongoose from 'mongoose';

const userSchema = mongoose.Schema({  
    "firstName": { type: String, required: true},
    "email": { type: String, required: true, unique: true},
    "ProfilePicture": { type: String},
})

var User = mongoose.model('Users', userSchema);

export default User;

Enter fullscreen mode Exit fullscreen mode

6. Front-End

import React, { useState } from "react";
import { Button, Form } from "react-form-elements";


function EditProfile() {
  const [fileData, setFileData] = useState("");

  const fileChangeHandler = (e) => {
    setFileData(e.target.files[0]);
  };

  const onSubmitHandler = () => {
    if (
      (fileData && fileData.type === "image/png") ||
      fileData.type === "image/jpeg" ||
      fileData.type === "image/jpg"
    ) {

      const data = new FormData();
      data.append("ProfilePicture", fileData);

      fetch(
        `http://localhost:5000/my_api/user/${localStorage.getItem(
          "userID"
        )}`,
        {
          method: "PATCH",
          body: data,
        }
      )
        .then((result) => {
          console.log("File Sent Successful");
        })
        .catch((err) => {
          console.log(err.message);
        });
    }
  };


  return (
    <div>
      <Form onSubmit={ onSubmitHandler } name="edit profile form">
        <input type="file" onChange={fileChangeHandler} />
        <Button type="submit" className="profile-order-button">
          Save Changes
        </Button>
       </Form>
    </div>
  );
}

export default EditProfile;


Enter fullscreen mode Exit fullscreen mode
Important notes:

data.append("ProfilePicture", fileData);

Make sure to spell the first argument in the append which is in this case "ProfilePicture" same as you spelled it in the last line of the upload.js file.

Top comments (3)

Collapse
 
andrewbaisden profile image
Andrew Baisden

Cool article by the way you can add syntax highlighting to your code blocks

Creating and highlighting code blocks

Collapse
 
singhanuj620 profile image
Anuj Singh

Nice advice ✌✌

Collapse
 
aimalm profile image
Aimal Maarij

Thank you @Andrewba