簡體   English   中英

文件上傳錯誤 - 適用於 Postman 但不適用於前端

[英]File Upload Error - Works in Postman but not on frontend

我正在通過 devchallenges.io 上的全棧認證,並且正在做身份驗證應用程序挑戰 到目前為止,我已經能夠創建登錄和注冊功能,並且能夠設置功能以獲取登錄用戶並顯示他們的信息,但是,當嘗試在前端上傳文件時,圖像上傳不會t 似乎工作。 如本視頻所示,它在 Postman 中運行良好。 在前端,其他字段似乎得到了更新,例如姓名、簡歷。 這里的錯誤示例。

Github 源代碼: https://github.com/gbopola/Auth-App

服務器.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}`));

驗證 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>
  );
};

通常您不會將圖像發送給用戶,您只需發送導致圖像的url。

通常將圖像傳遞到后端不起作用,除非您將其 append 傳遞給新的表單數據 -------------示例----- const formData = new FormData() formData.append('image', image)

然后將 formData 作為 object 發送

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM