文件上传错误 - 在 Postman 中有效,但在前端无效
Posted
技术标签:
【中文标题】文件上传错误 - 在 Postman 中有效,但在前端无效【英文标题】:File Upload Error - Works in Postman but not on frontend 【发布时间】:2022-01-09 21:08:19 【问题描述】:我正在通过 devchallenges.io 上的全栈认证,我正在做 authentication app challenge。到目前为止,我已经能够创建登录和注册功能,并且能够设置功能以获取登录用户并显示他们的信息,但是,当尝试在前端上传文件时,图像上传不会t 似乎工作。如video 所示,它在 Postman 中运行良好。在前端,其他字段似乎得到了更新,例如姓名、简历。错误here的示例。
Github 源码: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`));
验证 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>
);
;
【问题讨论】:
您没有将内容类型指定为multipart/form-data
。这会很有帮助***.com/questions/39663961/…
【参考方案1】:
除非您将图像附加到新的表单数据中,否则通常无法将图像传递到后端
例子:
const formData = new FormData();
formData.append('image', image);
然后将 formData 作为对象发送
【讨论】:
非常感谢,伙计 :)【参考方案2】:通常您不会将图像发送给用户,您只需发送指向图像的 url。
【讨论】:
感谢您的反馈。这正是我所做的。我将图像发送到 cloudinary。以上是关于文件上传错误 - 在 Postman 中有效,但在前端无效的主要内容,如果未能解决你的问题,请参考以下文章
我的请求在 Postman 中有效,但在浏览器中无效(React、Node、Cloudinary)
Workday 请求在 SoapUI 中有效,但在 Postman 中无效
HTTP 请求在 Postman 中有效,但在 C# 代码中无效
HTTP POST 请求在 Postman 中有效,但在代码中无效