尝试使用 MERN 上传图像时出现 400 错误请求错误
Posted
技术标签:
【中文标题】尝试使用 MERN 上传图像时出现 400 错误请求错误【英文标题】:Trying to upload an image using MERN getting 400 bad request error 【发布时间】:2021-05-13 10:00:46 【问题描述】:您好,希望有人可以提供帮助。使用MERN构建项目,需要有文件上传功能。已尝试实施各种解决方案,但似乎无济于事。任何帮助将不胜感激。
这是我的特快路线
const express = require('express');
const router = express.Router();
const auth = require('../../middleware/auth');
const check, validationResult = require('express-validator');
// bring in normalize to give us a proper url, regardless of what user entered
const normalize = require('normalize-url');
const checkObjectId = require('../../middleware/checkObjectId');
const path = require('path');
const multer = require('multer');
const Profile = require('../../models/Profile');
const User = require('../../models/User');
const Post = require('../../models/Post');
const upload = multer(
storage: multer.diskStorage(
destination(req, res, cb)
cb(null, './files');
,
filename(req, file, cb)
cb(null, `$new Date().getTime()_$file.originalname`);
),
limits:
fileSize: 1000000 // max file size 1MB = 1000000 bytes
,
fileFilter(req, file, cb)
if (!file.originalname.match(/\.(jpeg|jpg|png)$/))
return cb(
new Error(
'only upload files with jpg, jpeg, format.'
)
);
cb(undefined, true); // continue with upload
);
// @route POST api/profile/upload
// @desc Upload profile image
// @access Private
router.post(
'/upload',
upload.single('file'),
async (req, res) =>
try
console.log('Hello')
const path, mimetype, originalname = req.file;
const profileFields = ;
profileFields.user = req.user.id;
if (path) profileFields.image.path = path;
if (mimetype) profileFields.image.mimetype = mimetype;
if (originalname) profileFields.image.originalname = originalname;
let profile = await Profile.findOneAndUpdate(
user: req.user.id ,
$set: profileFields ,
new: true, upsert: true, setDefaultsOnInsert: true
);
return res.json(profile);
catch (err)
console.log('help')
res.sendStatus(400).send('Error while uploading file. Please try again later.')
,
(error, req, res, next) =>
if (error)
res.status(500).send(error.message);
);
这是我的模型
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema(
user:
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
,
location:
type: String,
required: true
,
bio:
type: String
,
image:
origianlname:
type: String,
required: true
,
path:
type: String,
required: true
,
mineType:
type: String,
required: true
,
,
topics: [
type: mongoose.Schema.Types.ObjectId,
ref: 'topic',
],
social:
youtube:
type: String
,
twitter:
type: String
,
facebook:
type: String
,
linkedin:
type: String
,
instagram:
type: String
,
date:
type: Date,
default: Date.now
);
module.exports = mongoose.model('profile', ProfileSchema);
我在后端设置了一个名为 files 的文件夹
前端
动作文件
//Upload profile picture
export const uploadImage = (file) => async (dispatch) =>
try
const formData = new FormData();
formData.append('file', file);
await axios.post('api/profile/upload', formData,
headers:
'Content-Type': 'multipart/form-data'
);
dispatch(setAlert('Profile picture uploaded successfully', 'success'))
catch (err)
dispatch(
type: PROFILE_ERROR,
payload: msg: err.response.statusText, status: err.response.status
);
上传.js 组件文件
import React, useState, useEffect from 'react';
import connect from 'react-redux';
import Form, Button from 'react-bootstrap';
import uploadImage from '../../actions/profile';
const Upload = ( errors, dispatch ) =>
const [file, setFile] = useState(null);
const [isSubmitted, setIsSubmitted] = useState(false);
const [errorMsg, setErrorMsg] = useState(null);
useEffect(() =>
setErrorMsg(errors);
, [errors]);
useEffect(() =>
setErrorMsg('');
, [])
const handleOnChange = (event) =>
const file = event.target.files[0];
setFile(file);
;
const handleFormSubmit = (event) =>
event.preventDefault();
if (file)
setErrorMsg('');
dispatch(uploadImage(file));
setIsSubmitted(true);
;
return (
<React.Fragment>
errorMsg && errorMsg.upload_error ? (
<p className="errorMsg centered-message">errorMsg.upload_error</p>
) : (
isSubmitted && (
<p className="successMsg centered-message">
Photo uploaded successfully
</p>
)
)
<Form
onSubmit=handleFormSubmit
method="post"
encType="multipart/form-data"
className="upload-form"
>
<Form.Group>
<Form.Label>Choose photo to upload</Form.Label>
<Form.Control type ="file" name="file" onChange=handleOnChange />
</Form.Group>
<Button
variant="primary"
type="submit"
className=`$!file ? 'disabled submit-btn' : 'submit-btn'`
>
Upload
</Button>
</Form>
</React.Fragment>
);
;
const mapStateToProps = (state) => (
file: state.file || [],
errors: state.errors ||
);
export default connect(mapStateToProps)(Upload);
减速器文件
import
GET_PROFILE,
PROFILE_ERROR,
CLEAR_PROFILE,
GET_PROFILES,
from '../actions/types';
const initialState =
profile: null,
profiles: [],
repos: [],
loading: true,
error:
;
function profileReducer(state = initialState, action)
const type, payload = action;
switch (type)
case GET_PROFILE:
return
...state,
profile: payload,
loading: false
;
case GET_PROFILES:
return
...state,
profiles: payload,
loading: false
;
case PROFILE_ERROR:
return
...state,
error: payload,
loading: false,
profile: null
;
case CLEAR_PROFILE:
return
...state,
profile: null,
repos: []
;
default:
return state;
export default profileReducer;
希望有人能看到我哪里出错了。我在 redux devtools 上的操作负载说 PROFILE_ERROR 正在运行,我收到状态 400 错误请求。
【问题讨论】:
您的模型中有一个错字 originalname 拼写错误,这肯定会引发错误 谢谢你已经修复它,但还没有解决问题 【参考方案1】:所以我玩过,现在有一个可行的解决方案。将其发布在此处,以供需要了解如何执行此操作的其他人使用。
这里是特快路线
const express = require('express');
const router = express.Router();
const auth = require('../../middleware/auth');
const check, validationResult = require('express-validator');
// bring in normalize to give us a proper url, regardless of what user entered
const normalize = require('normalize-url');
const checkObjectId = require('../../middleware/checkObjectId');
const path = require('path');
const multer = require('multer');
const Profile = require('../../models/Profile');
const User = require('../../models/User');
const Post = require('../../models/Post');
const upload = multer(
storage: multer.diskStorage(
destination(req, file, cb)
cb(null, 'uploads');
,
filename(req, file, cb)
cb(null, `$new Date().getTime()_$file.originalname`);
),
limits:
fileSize: 1024 * 1024 * 5
,
fileFilter(req, file, cb)
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png' || file.mimetype === 'image/gif' || file.mimetype === 'image/jpg' || file.mimetype ==='image/jfif')
cb(null, true)
else
cb(new Error('Please upload a file type of jpeg, png or gif'), false)
);
// @route POST api/profile
// @desc Create or update user profile
// @access Private
router.post(
'/',
upload.single('file'),
auth,
check('location', 'Location is required').not().isEmpty(),
async (req, res) =>
const errors = validationResult(req);
if (!errors.isEmpty())
return res.status(400).json( errors: errors.array() );
console.log(req.file);
console.log(req.body);
// destructure the request
const
location,
bio,
youtube,
twitter,
instagram,
linkedin,
facebook,
= req.body;
// build a profile
const profileFields = ;
profileFields.user = req.user.id;
if(req.file !== undefined)
let fileUrl = "/" + req.file.path.replace(/\\/g, "/");
if (path) profileFields.path = fileUrl;
if (mimetype) profileFields.mimeType = req.file.mimetype;
if (originalname) profileFields.orginalName = req.file.originalname;
if (location) profileFields.location = req.body.location;
if (bio) profileFields.bio = req.body.bio;
else
if (location) profileFields.location = req.body.location;
if (bio) profileFields.bio = req.body.bio;
// Build social object
const socialFields = youtube, twitter, instagram, linkedin, facebook
for (const [key, value] of Object.entries(socialFields))
if (value && value.length > 0)
socialFields[key] = normalize(value, forceHttps: true );
try
// Using upsert option (creates new doc if no match is found):
let profile = await Profile.findOneAndUpdate(
user: req.user.id ,
$set: profileFields ,
new: true, upsert: true, setDefaultsOnInsert: true
);
return res.json(profile);
catch (err)
console.error(err.message);
return res.status(500).send('Server Error');
);
这是模型
const mongoose = require('mongoose');
const ProfileSchema = new mongoose.Schema(
user:
type: mongoose.Schema.Types.ObjectId,
ref: 'user'
,
location:
type: String,
required: true
,
bio:
type: String
,
orginalName:
type: String,
required: true
,
path:
type: String,
required: true
,
mimeType:
type: String,
required: true
,
topics: [
type: mongoose.Schema.Types.ObjectId,
ref: 'topic',
],
social:
youtube:
type: String
,
twitter:
type: String
,
facebook:
type: String
,
linkedin:
type: String
,
instagram:
type: String
,
date:
type: Date,
default: Date.now
);
module.exports = mongoose.model('profile', ProfileSchema);
这是动作文件
// Create or update profile
export const createProfile = (payload, history, edit = false) => async (
dispatch
) =>
try
const res = await api.post('/profile', payload,
headers:
'Content-Type': 'multipart/form-data'
);
dispatch(
type: GET_PROFILE,
payload: res.data
);
dispatch(setAlert(edit ? 'Profile Updated' : 'Profile Created', 'success'));
if (!edit)
history.push('/dashboard');
catch (err)
const errors = err.response.data.errors;
if (errors)
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger')));
dispatch(
type: PROFILE_ERROR,
payload: msg: err.response.statusText, status: err.response.status
);
;
这里是减速器
import
GET_PROFILE,
PROFILE_ERROR,
CLEAR_PROFILE,
GET_PROFILES,
from '../actions/types';
const initialState =
profile: null,
profiles: [],
loading: true,
error:
;
function profileReducer(state = initialState, action)
const type, payload = action;
switch (type)
case GET_PROFILE:
return
...state,
profile: payload,
loading: false
;
case GET_PROFILES:
return
...state,
profiles: payload,
loading: false
;
case PROFILE_ERROR:
return
...state,
error: payload,
loading: false,
profile: null
;
case CLEAR_PROFILE:
return
...state,
profile: null,
;
default:
return state;
export default profileReducer;
这里是上传组件
import React, useEffect, useState, Fragment from 'react';
import Link, withRouter, Redirect from 'react-router-dom';
import PropTypes from 'prop-types';
import connect from 'react-redux';
import createProfile, getCurrentProfile from '../../actions/profile';
const CreateProfile = (
createProfile,
getCurrentProfile,
profile: profile, loading ,
history
) =>
const [formData, setFormData] = useState(
location: '',
bio: '',
twitter: '',
facebook: '',
linkedin: '',
youtube: '',
instagram: ''
);
const [file, setFile] = useState("");
const [imageName, setImageName] = useState("Choose file");
const [displaySocialInputs, toggleSocialInputs] = useState(false);
const
location,
bio,
twitter,
facebook,
linkedin,
youtube,
instagram
= formData;
const onFileChange = (e) =>
setFile(e.target.files[0]);
setImageName(e.target.files[0].name);
const onChange = (e) =>
setFormData( ...formData, [e.target.name]: e.target.value );
;
const onSubmit = (e) =>
e.preventDefault();
const payload = new FormData();
payload.append("file", file);
payload.append("location", formData.location);
payload.append("bio", formData.bio);
payload.append("twitter", formData.twitter);
payload.append("facebook", formData.facebook);
payload.append("linkedin", formData.linkedin);
payload.append("youtube", formData.youtube);
payload.append("instagram", formData.instagram);
createProfile(payload, history);
;
useEffect(() =>
getCurrentProfile();
// eslint-disable-next-line react-hooks/exhaustive-deps
, [getCurrentProfile]);
return loading && profile === null ? (
<Redirect to='/dashboard' />
) : (
<Fragment>
<h1 className='large text-primary'>Create Your Profile</h1>
<p className='lead'>
<i className='fas fa-user' /> Let's get some information to make your
profile stand out
</p>
<small>* = required field</small>
<form className='form' onSubmit=e => onSubmit(e) encType="multipart/form-data">
<div className='form-group'>
<input
type='text'
placeholder='Location'
name='location'
value=location
onChange=(e) => onChange(e)
/>
<small className='form-text'>
City (eg. Glasgow)
</small>
</div>
<div className='form-group'>
<textarea
placeholder='A short bio of yourself'
name='bio'
value=bio
onChange=(e) => onChange(e)
/>
<small className='form-text'>Tell us a little about yourself</small>
</div>
<div className="form-group">
<label htmlFor="image">Upload Profile Image</label><br></br>
<input type="file" onChange=(e) => onFileChange(e) accept="image/*" />
</div>
<div className='my-2'>
<button
onClick=() => toggleSocialInputs(!displaySocialInputs)
type='button'
className='btn btn-light'
>
Add Social Network Links
</button>
<span>Optional</span>
</div>
displaySocialInputs && (
<Fragment>
<div className='form-group social-input'>
<i className='fab fa-twitter fa-2x' />
<input
type='text'
placeholder='Twitter URL'
name='twitter'
value=twitter
onChange=e => onChange(e)
/>
</div>
<div className='form-group social-input'>
<i className='fab fa-facebook fa-2x' />
<input
type='text'
placeholder='Facebook URL'
name='facebook'
value=facebook
onChange=e => onChange(e)
/>
</div>
<div className='form-group social-input'>
<i className='fab fa-youtube fa-2x' />
<input
type='text'
placeholder='YouTube URL'
name='youtube'
value=youtube
onChange=e => onChange(e)
/>
</div>
<div className='form-group social-input'>
<i className='fab fa-linkedin fa-2x' />
<input
type='text'
placeholder='Linkedin URL'
name='linkedin'
value=linkedin
onChange=e => onChange(e)
/>
</div>
<div className='form-group social-input'>
<i className='fab fa-instagram fa-2x' />
<input
type='text'
placeholder='Instagram URL'
name='instagram'
value=instagram
onChange=e => onChange(e)
/>
</div>
</Fragment>
)
<input type='submit' className='btn btn-primary my-1' />
<Link className='btn btn-light my-1' to='/dashboard'>
Go Back
</Link>
</form>
</Fragment>
);
;
CreateProfile.propTypes =
createProfile: PropTypes.func.isRequired,
getCurrentProfile: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired
;
const mapStateToProps = state => (
profile: state.profile
);
export default connect(mapStateToProps, createProfile, getCurrentProfile )(withRouter(CreateProfile));
希望这对遇到类似问题的人有所帮助
【讨论】:
以上是关于尝试使用 MERN 上传图像时出现 400 错误请求错误的主要内容,如果未能解决你的问题,请参考以下文章
从 React 前端将图像上传到烧瓶后端时出现 KeyError 'image'
尝试通过 XCode 上传二进制文件时出现“无效的图像路径”错误