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
Setting up the frontend part
First cd into the frontend folder
cd frontend
Then set up your react app
npx create-react-app .
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
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;
Import the User component inside App.js
import './App.css';
import './User';
function App() {
return (
<User />
);
}
export default App;
Setting up the backend part
In the backend folder, we need to upload the needed packages:
npm i express cors mongoose multer uuid
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}`)
})
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;
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;
And that's it! that's how you upload an image to the server.
Top comments (15)
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.
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);
req.file undefined
Make sure to add encType='multipart/form-data'
a few typos to fix :
setNewAuthor is not defined .. Where is that useState defined actually?
setNewAuthor = setNewUser
Thanks :)
req.file undefined
Make sure to add encType='multipart/form-data'
I added this still getting the error and also I'm not able to upload any image
This is what I needed.Thanks a lot,Ms. Skhiri.
req.file.filename show undefiend while post request in frontend . How can i solve this issue
How could to upload multiple images