menu

Questions & Answers

Image not being sent in payload using Multer in Nodejs and ReactJs

I am sending an image from my reactjs frontend to my nodejs express backend using formData.

But when i am appending the image it doesn't appear in the payload and i get this error from the backend. TypeError: Cannot read properties of undefined (reading 'filename')

I'm sending the data seemingly correct, so i dont understand why the image is not being sent to the backend in the payload.

this is the payload i am getting. enter image description here

Here is my code:

Backend

user.js:

const MIME_TYPES = {
    'image/png': 'png',
    'image/jpeg': 'jpg',
    'image/jpg': 'jpg',
}
const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, "images");
    },
    filename: (req, file, cb) => {
       const name = file.originalname.toLowerCase().split(" ").join("-");
       const extension = MIME_TYPES[file.mimetype];
       cb(null, name + "-" + Date.now() + "." + extension);
    }
})


router.post("/register", multer({ storage: storage }).single("uniIDImage"), (req, res, next) => {

    const url = req.protocol + "://" + req.get("host");
    const fullUrl = url + "/images/" + req.file.filename;
    let User = new models.User();

        User.register(req.body, fullUrl).then((response) => {
            console.log(response);
            if(response) {
                return res.json({
                    msg: 'User registered successfully.',
                    statusCode:200,
                    result: response,
                });
            }
  });
});

Reactjs frontend:

registerActions.js:

export const register = (formData) => async dispatch => {

    const config = {
        headers: {
            'Content-Type': 'multipart/form-data;',
        }
    }

    let jsonObject = {};
    for (let key of formData.keys()) {
        jsonObject[key] = formData.get(key);
        console.log( jsonObject[key])
    }

    try {
        const res = await axios.post('/register', jsonObject, config);
        dispatch({
            type: REGISTER_SUCCESS,
            payload: res.data
        });
    } catch (err) {
        dispatch({
            type:REGISTER_FAIL,
        });
    }
}

Register.js

import React, { useEffect, useReducer, useState } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
import { register } from '../../store/auth/register/actions'

const Register = ({ register }) => {

  const [uniIDImage, setUniIDImage] = useState([]);
  const [data, setData] = useState({
    name: '',
    uniID: '',
    username: '',
    email: '',
    phoneNumber: '',
    password: '',
  });

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

  const handleImage = (e) => {
    setUniIDImage({ pic: e.target.files[0] });
  }

    const signUp = (e) => {
        e.preventDefault();
    
        const formData = new FormData()
        formData.append("name", data.name);
        formData.append("uniID", data.uniID);
        formData.append("username", data.username);
        formData.append("email", data.email);
        formData.append("phoneNumber", data.phoneNumber);
        formData.append("uniIDImage", uniIDImage.pic);
        formData.append("password", data.password);
        console.log(uniIDImage.pic);
        register(formData);
      }

return (
<div className="mb-3">
                          <Label className="form-label">ID Photo</Label>
                          <Input
                              id="fileInput"
                              name="uniIDImage"
                              className="form-control"
                              accept="image/*"
                              type="file"
                              onChange={handleImage}
                          />
)
    
Register.propTypes = {
  register: PropTypes.func.isRequired,
}
export default connect(null, { register })(Register);
Answers(1) :

When you convert form data to JSON object, you lose the file, and there is no point converting it, and also no need to set content-type header.

So, simply pass formData to axios as a second argument:

export const register = (formData) => async dispatch => {

    try {
        const res = await axios.post('/register', formData);
        dispatch({
            type: REGISTER_SUCCESS,
            payload: res.data
        });
    } catch (err) {
        dispatch({
            type: REGISTER_FAIL,
        });
    }
}