Home > Back-end >  File Upload Error - Works in Postman but not on frontend
File Upload Error - Works in Postman but not on frontend

Time:12-03

I am working through the full stack certification on devchallenges.io and I'm doing the authentication app challenge. So far I have been able to create the login and register functionality and have been able to set up the functionality to get the logged in user and display their information however, when trying to upload a file on the front end, the image upload doesn't seem to work. It works perfectly fine in Postman as shown in this video. On the front end, other fields seem to get updated such as the name, bio. Example of the error here.

Github source code: https://github.com/gbopola/Auth-App

server.js

const express = require('express');
const connectDB = require('./config/db');
const app = express();
const { check, validationResult } = require('express-validator');
const User = require('./models/User');
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const config = require('config');
const auth = require('./middleware/auth');
const cloudinary = require('./utils/cloudinary');
const upload = require('./utils/multer');

// Connect database
connectDB();

// Init Middleware
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: true }));

// @route    POST /register
// @desc     Register user
// @access   Public
app.post(
  '/register',
  [
    check('email', 'Please include a valid email').isEmail(),
    check('password', 'Please enter a password').notEmpty(),
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    const { email, password } = req.body;

    try {
      // See if user exists
      let user = await AuthUser.findOne({ email });

      if (user) {
        return res
          .status(400)
          .json({ errors: [{ msg: 'User already exists' }] });
      }
      // Get users gravatar
      const avatar = gravatar.url(email, {
        s: '200',
        r: 'pg',
        d: 'mm',
      });

      user = new AuthUser({
        email,
        avatar,
        password,
      });

      // Encrypt password
      const salt = await bcrypt.genSalt(10);

      user.password = await bcrypt.hash(password, salt);

      await user.save();

      // Return jsonwebtoken
      const payload = {
        user: {
          id: user.id,
        },
      };

      jwt.sign(
        payload,
        config.get('jwtSecret'),
        { expiresIn: '5 days' },
        (err, token) => {
          if (err) throw err;
          res.json({ token });
        }
      );
    } catch (error) {
      console.error(error.message);
      res.status(500).send('Server error');
    }
  }
);

// @route    POST /login
// @desc     Authenticate user & get token
// @access   Public

app.post(
  '/login',
  check('email', 'Please include a valid email').isEmail(),
  check('password', 'Password is required').exists(),
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({
        errors: errors.array(),
      });
    }

    const { email, password } = req.body;

    try {
      // See if user exists
      let user = await User.findOne({ email });

      if (!user) {
        return res
          .status(400)
          .json({ errors: [{ msg: 'Invalid credentials' }] });
      }

      const isMatch = await bcrypt.compare(password, user.password);

      if (!isMatch) {
        return res
          .status(400)
          .json({ errors: [{ msg: 'Invalid credentials' }] });
      }

      // Return jsonwebtoken
      const payload = {
        user: {
          id: user.id,
        },
      };

      jwt.sign(
        payload,
        config.get('jwtSecret'),
        { expiresIn: '5 days' },
        (err, token) => {
          if (err) throw err;
          res.json({ token });
        }
      );
    } catch (err) {
      console.error(err.message);
      res.status(500).send('Server error');
    }
  }
);

// @route    GET /profile
// @desc     Get full user profile
// @access   Private

app.get('/profile', auth, async (req, res) => {
  try {
    let user = await User.findById(req.user.id).select('-password');

    res.json(user);
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server error');
  }
});

// @route    POST /profile/edit/:id
// @desc     edit profile
// @access   Private

app.put('/profile/edit/:id', upload.single('image'), auth, async (req, res) => {
  const { name, bio, email, phone, password } = req.body;

  try {
    let user = await AuthUser.findById(req.params.id);

    // Delete image from cloudinary
    if (user.cloudinary_id !== '')
      await cloudinary.uploader.destroy(user.cloudinary_id);

    // Upload image to cloudinary
    let result;
    if (req.file) {
      result = await cloudinary.uploader.upload(req.file.path);
    }

    const data = {
      name: name || user.name,
      avatar: (result && result.secure_url) || user.avatar,
      bio: bio || user.bio,
      email: email || user.email,
      phone: phone || user.phone,
      password: password || user.password,
      cloudinary_id: (result && result.public_id) || user.cloudinary_id,
    };

    if (password !== '') {
      // Encrypt password
      const salt = await bcrypt.genSalt(10);

      data.password = await bcrypt.hash(password, salt);
    }

    //   Update
    user = await User.findByIdAndUpdate(req.params.id, data, { new: true });

    return res.json(data);
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server error');
  }
});

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

app.listen(PORT, () => console.log(`Server started on port ${PORT}`));

Auth Action.js

// Update use profile
export const updateProfile = ({
  name,
  bio,
  phone,
  email,
  password,
  id,
  profileImg,
  navigate,
}) => {
  return async (dispatch) => {
    const config = {
      headers: {
        'Content-Type': 'application/json',
      },
    };
    const body = JSON.stringify({
      name,
      bio,
      phone,
      email,
      password,
      id,
      profileImg,
    });

    try {
      const res = await axios.put(`/profile/edit/${id}`, body, config);

      dispatch({
        type: PROFILE_UPDATE_SUCCESS,
        payload: res.data,
      });

      navigate('/profile');
    } catch (error) {
      console.log(error);
    }
  };
};

import React, { useEffect, useState, useRef } from 'react';
import { Navbar } from './Navbar';
import { useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import { loadUser } from '../redux/actions/auth';
import { Link, useParams, useNavigate } from 'react-router-dom';
import { store } from '../store';
import { updateProfile } from '../redux/actions/auth';
export const EditProfile = () => {
  const state = useSelector((state) => state.auth);
  const { id } = useParams();
  const dispatch = useDispatch();
  const navigate = useNavigate();
  // States
  const [isEditing, setEdit] = useState(false);
  const [profileImg, setImg] = useState(state.user.avatar);
  const [formData, setFormData] = useState({
    name: '',
    bio: '',
    phone: '',
    email: '',
    password: '',
  });

  const { email, password, bio, phone, name } = formData;

  const inputFile = useRef(null);

  let styles = {
    width: '72px',
    height: '72px',
    borderRadius: '8px',
    backgroundImage: `url(${!isEditing ? state.user.avatar : profileImg})`,
    backgroundPosition: 'center',
    backgroundSize: 'cover',
    position: 'relative',
  };

  // handle image change
  const imageHandler = (e) => {
    const reader = new FileReader();
    reader.onload = () => {
      if (reader.readyState === 2) {
        setImg(reader.result);
        setEdit(true);
      }
    };
    if (e.target.files[0]) {
      reader.readAsDataURL(e.target.files[0]);
    }
  };

  const changePhoto = () => {
    inputFile.current.click();
  };

  const onChange = (e) => {
    setFormData({ ...formData, [e.target.name]: e.target.value });
  };

  const changeInfo = () => {
    dispatch(
      updateProfile({
        name,
        bio,
        phone,
        email,
        password,
        id,
        profileImg,
        navigate,
      })
    );
  };

  return (
    <div className="EditProfile">
      <Navbar />
      <div className="edit-profile-container">
        <div className="back-to-profile">
          <Link className="link-to-profile" to="/profile">
            <span>
              <i className="fas fa-chevron-left"></i>
            </span>
            Back
          </Link>
        </div>
        <div className="profile-wrapper">
          <div className="profile-heading">
            <div>
              <h2>Change Info</h2>
              <p className="personal-info-grey">
                Changes will be reflected to every services
              </p>
            </div>
          </div>
          <div className="profile-photo">
            <input
              type="file"
              accept="image/*"
              name="image-upload"
              id="upload"
              onChange={imageHandler}
              ref={inputFile}
            />
            <div className="example" onClick={changePhoto}>
              <i className="fas fa-camera"></i>
              <div id="overlay"></div>
              <div id="profile-img-edit" style={styles}></div>
            </div>
            <p className="personal-info-grey change-photo">CHANGE PHOTO</p>
          </div>
          <div className="name">
            <label>Name</label>
            <input
              type="text"
              className="edit-profile-input"
              placeholder="Enter your name"
              name="name"
              value={name}
              onChange={(e) => onChange(e)}
            />
          </div>
          <div className="bio">
            <label>Bio</label>
            <textarea
              className="edit-profile-input"
              id="bio"
              placeholder="Enter your bio"
              name="bio"
              value={bio}
              onChange={(e) => onChange(e)}
            />
          </div>
          <div className="phone">
            <label>Phone</label>
            <input
              type="text"
              className="edit-profile-input"
              placeholder="Enter your phone"
              name="phone"
              value={phone}
              onChange={(e) => onChange(e)}
            />
          </div>
          <div className="email">
            <label>Email</label>
            <input
              type="text"
              className="edit-profile-input"
              placeholder="Enter your email"
              name="email"
              value={email}
              onChange={(e) => onChange(e)}
            />
          </div>
          <div className="password">
            <label>Password</label>
            <input
              type="password"
              className="edit-profile-input"
              placeholder="Enter your password"
              name="password"
              value={password}
              onChange={(e) => onChange(e)}
            />
            <button className="edit-save" onClick={changeInfo}>
              Save
            </button>
          </div>
        </div>
      </div>
    </div>
  );
};

CodePudding user response:

Usually you don't send the image to the user, you just send the url that leads to the image.

CodePudding user response:

Normally passing images to the backend does not work unless you append it to a new form data -------------Example----------------- const formData = new FormData() formData.append('image', image)

then send formData as an object

  • Related