Ethan
Ethan

Reputation: 47

React Hooks and Axios Image Upload with Multer

I am trying to add the ability to upload an image and then update a user object with that image path. When I upload the image using Insomnia client and send the request, I am able to successfully add the image and path to MongoDB. NodeJS user route below:

const express = require ("express"); 
const router = express.Router(); 
const bcrypt = require("bcryptjs"); 
const jwt = require("jsonwebtoken"); 
const Users = require("../models/users"); 
const auth = require("../middleware/auth"); 
const multer = require('multer');

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

const fileFilter = (req, file, cb) => {
    // reject a file
    if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png' || file.mimetype == "image/jpg") {
      cb(null, true);
        } else {
            cb(null, false);
            return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
        }
  };

const upload = multer({storage: storage, limits: {
    fileSize: 1024 * 1024 * 5 
    }, 

    fileFilter: fileFilter
});
// REQUEST TO FIND USER BY ID AND ADD A PROFILE PICTURE 
router.put('/update/:id', upload.single('userImage'), (req, res) => {
    Users.findById(req.params.id)
    .then(user => {
        user.userImage = req.file.path || user.userImage,

        user 
            .save()
            .then(() => res.json("The User is UPDATED succesfully!"))
            .catch(err => res.status(400).json(`Error: ${err}`)); 
        }) 
    .catch(err => res.status(500).json(`Error: ${err}`));  
    console.log(req.body); 
    console.log(req.file); 

});

However, when I test the code on the client I receive the following 500 error: ""Error: TypeError: Cannot read property 'path' of undefined." I am using React Hooks and Axios, code below:

import React, { useState, useContext } from 'react'; 
import Exit from '../cancel.svg'; 
import Modal from 'react-modal';
import { useForm } from "react-hook-form";
import axios from 'axios';
import UserContext from "../context/UserContext"; 
import { useHistory } from "react-router-dom";


Modal.setAppElement('#root');

const ProfilePicture = () => {
    const [modalIsOpen, setModalIsOpen]= useState (true);
    const { register, handleSubmit } = useForm (); 
    const [userImage, setUserImage] = useState(); 

    const onSubmit = (data) => console.log(data);

    const { userData } = useContext(UserContext); 
    const history = useHistory();


    const changeOnClick = e => {
        const users = {
            userImage
        }; 

        console.log(users); 

         axios
            .put(`http://localhost:5000/users/update/${userData.user.id}`, users)
            .then(res => console.log(res.body))
            .catch(err => {
                console.log(err); 
            }); 
            history.push("/Landing")
    } 

return(
<Modal isOpen={modalIsOpen} onRequestClose={() => setModalIsOpen(false)} className='finishBorder'> 
    <div className='border-bottom-two'>
        <img onClick= {() => setModalIsOpen(false)} className='newexitButton'src={Exit} alt='X' />
            <span className='lastThing'>One last thing! Add a profile picture</span>
    </div>
    <form onSubmit={handleSubmit(changeOnClick)} encType="multipart/form-data" >
        <div>
            <span className="dot"></span>
            <input onChange={e => setUserImage(e.target.value)} ref = {register} type='file' name='userImage' className='picUploader'/>
            {/* <button>submit</button> */}
        </div>
        <div>
            <button type='submit' className='doneButton'>Done</button>
                <div>
                    <span className='laterTwo'>I'll do this later</span>
                </div>
        </div>
    </form>
</Modal>
)

}

export default ProfilePicture; 

Appreciate the help!

Upvotes: 2

Views: 4783

Answers (1)

Gaston Laudin
Gaston Laudin

Reputation: 31

What you are sending from the client is a "fakepath", not a file. To access the file from the input use:

setUserImage(e.target.files[0])

Also, I think multer only works with multipart/form-data, so you should check that, but is as easy as:

const formData = new FormData();
formData.append([image name], [image file]);
axios.post(`http://localhost:5000/users/update/${userData.user.id}`, formData, { 
headers: { "Content-Type": "multipart/form-data" }})

Upvotes: 3

Related Questions