DEV Community

Yosra Skhiri
Yosra Skhiri

Posted on

How to upload an image using MERN stack

I'm currently learning the MERN stack by making a project, one of this project's functionalities is to create a user profile, by doing so I needed to implement a file input in the form in order to add the user's profile picture, and this is where I got stuck! the process of uploading a photo seemed complicated but don't worry I figured out (hence the title).

Setting up the project

Inside the project's folder, I made two folders: frontend and backend

|
|-- project
    |
    |-- frontend
    |-- backend
Enter fullscreen mode Exit fullscreen mode

Setting up the frontend part

First cd into the frontend folder

cd frontend
Enter fullscreen mode Exit fullscreen mode

Then set up your react app

npx create-react-app .
Enter fullscreen mode Exit fullscreen mode

we're also going to upload the axios package which is a promise-based HTTP client for the browser and node.js.

npm i axios 
Enter fullscreen mode Exit fullscreen mode

Now let's get to coding, create a User.js component in the src folder.
the data that we're sending to the server includes name, birth date, and a photo that could only be a png, jpg, or jpeg.

import React, { useState } from 'react';
import axios from 'axios';

const User = () => {
    const [newUser, setNewUser] = useState(
        {
            name: '',
            birthdate: '',
            photo: '',
        }
    );

    const handleSubmit = (e) => {
        e.preventDefault();
        const formData = new FormData();
        formData.append('photo', newUser.photo);
        formData.append('birthdate', newUser.birthdate);
        formData.append('name', newUser.name);

        axios.post('http://localhost:5000/users/add/', formData)
             .then(res => {
                console.log(res);
             })
             .catch(err => {
                console.log(err);
             });
    }

    const handleChange = (e) => {
        setNewAuthor({...newUser, [e.target.name]: e.target.value});
    }

    const handlePhoto = (e) => {
        setNewAuthor({...newUser, photo: e.target.files[0]});
    }

    return (
        <form onSubmit={handleSubmit} encType='multipart/form-data'>
            <input 
                type="file" 
                accept=".png, .jpg, .jpeg"
                name="photo"
                onChange={handlePhoto}
            />

            <input 
                type="text"
                placeholder="name"
                name="name"
                value={newUser.name}
                onChange={handleChange}
            />

            <input 
                type="date"
                name="birthdate"
                value={newUser.date}
                onChange={handleChange}
            />

            <input 
                type="submit"
            />
        </form>
    );
}

export default User;
Enter fullscreen mode Exit fullscreen mode

Import the User component inside App.js

import './App.css';
import './User';

function App() {
  return (
    <User />
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Setting up the backend part

In the backend folder, we need to upload the needed packages:

npm i express cors mongoose multer uuid
Enter fullscreen mode Exit fullscreen mode

Here's the reason why we need these packages:

  • express: is a minimalist web framework for node
  • cors: is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options, according to the cors package repository
  • mongoose: Mongoose is a MongoDB object modeling tool designed to work in an asynchronous environment.
  • multer: is a node.js middleware for handling multipart/form-data.
  • uuid: is a package that generates random and unique ids, I use it in this project to make sure that every uploaded image has a unique name.

create app.js

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

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

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

app.use(cors());
app.use(express.json());

const uri = process.env.ATLAS_URI;
mongoose.connect(uri, { useNewUrlParser: true, useCreateIndex: true, useUnifiedTopology: true});

const connection = mongoose.connection;
connection.once('open', () => {
    console.log('mongo DB success');
});

const userRouter = require('./routes/users');
app.use('/users', userRouter);

app.listen(port, () => {
    console.log(`Example app listening at http://localhost:${port}`)
})
Enter fullscreen mode Exit fullscreen mode

Create .env file and store in it the port and your atlas URI.
Make a routes folder and create user.js inside it; also create the images folder inside the backend folder.

const router = require('express').Router();
const multer = require('multer');
const { v4: uuidv4 } = require('uuid');
let path = require('path');
let User = require('../models/user.modal');

const storage = multer.diskStorage({
    destination: function(req, file, cb) {
        cb(null, 'images');
    },
    filename: function(req, file, cb) {   
        cb(null, uuidv4() + '-' + Date.now() + path.extname(file.originalname));
    }
});

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

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

router.route('/add').post(upload.single('photo'), (req, res) => {
    const name = req.body.name;
    const birthdate = req.body.birthdate;
    const photo = req.file.filename;

    const newUserData = {
        name,
        birthdate,
        photo
    }

    const newUser = new User(newUserData);

    newUser.save()
           .then(() => res.json('User Added'))
           .catch(err => res.status(400).json('Error: ' + err));
});

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

and don't forget to make the user.modal.js in the modals folder

const mongoose = require('mongoose');

const Schema = mongoose.Schema;

const userSchema = new Schema({
    name: {
        type: String,
        required: true,
        trim: true
    }, 

    photo: {
        type: String
    },

    birthdate: {
        type: String
    }
});

const User = mongoose.model('User', userSchema);

module.exports = User;
Enter fullscreen mode Exit fullscreen mode

And that's it! that's how you upload an image to the server.

Top comments (15)

Collapse
 
piyush0810 profile image
Piyush

In case we follow this for update functionality, how to delete the images stored. I can only change the name for new image and its reference. Also the mango db is not used I guess in this case.

Collapse
 
kaxiif profile image
Kashif Huusain

sure the image is not going to be stored in DB but the new unique name is going to be stored in database. Multer is storing the images in server folder... and you can delete images or any file by providing path and the name for that file can be access from database that you have stored when uploaded image

var fs = require('fs');
var filePath = 'c:/images/pic.png';
fs.unlinkSync(filePath);

Collapse
 
aniketrathi profile image
aniketrathi

req.file undefined

Collapse
 
2ndplayer profile image
Chris Camp

Make sure to add encType='multipart/form-data'

Collapse
 
jisooyu profile image
JS Yu

a few typos to fix :

  1. make the user.modal.js in the modals folder -->make the user.modal.js in the models folder
  2. in app.js of the backend ---> app.use(express.json({ extended: false })); mongoose.connect(uri, { useNewUrlParser: true, // useCreateIndex: true, useUnifiedTopology: true, });
  3. in App.js --> import User from './User';
  4. Make a routes folder and create user.js inside it. ---> Make a routes folder and create users.js inside it.
Collapse
 
kumarjayanth55 profile image
Jay

setNewAuthor is not defined .. Where is that useState defined actually?

Collapse
 
chioukhhmimi profile image
Chioukhhmimi

setNewAuthor = setNewUser

Collapse
 
kumarjayanth55 profile image
Jay

Thanks :)

Collapse
 
mukulrajpoot262610 profile image
Mukul Rajpoot

req.file undefined

Collapse
 
2ndplayer profile image
Chris Camp

Make sure to add encType='multipart/form-data'

Collapse
 
sanjana2109 profile image
Sanjana2109

I added this still getting the error and also I'm not able to upload any image

Collapse
 
jisooyu profile image
JS Yu

This is what I needed.Thanks a lot,Ms. Skhiri.

Collapse
 
tahoorahmed1 profile image
Tahoor Ahmed

req.file.filename show undefiend while post request in frontend . How can i solve this issue

Collapse
 
hellohowareu profile image
Şükürov Şükür

How could to upload multiple images