[英]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.