如何将图像从 React 和 Redux 传递到后端
How to pass an image to the backend from react and redux
我正在使用 MERN 堆栈,我希望能够在前端(反应)上传图像并在后端(express、nodejs)访问它以便稍后存储它。我正在使用 multer,但是当我尝试 console.log() req.file 对象时,我一直不确定。
前端:
import React, { Component } from "react";
import { Link, withRouter } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { addPlayer } from "../../actions/profileActions";
class AddPlayer extends Component {
constructor(props) {
super(props);
this.state = {
avatarFile: null,
name: "",
age: "",
apodo: "",
captain: false,
description: "",
atributes: "",
facebook: "",
instagram: "",
twitter: "",
youtube: "",
};
this.onSubmit = this.onSubmit.bind(this);
this.onImageChange = this.onImageChange.bind(this);
}
onSubmit(e) {
e.preventDefault();
const playerData = {
name: this.state.name,
age: this.state.age,
apodo: this.state.apodo,
captain: this.state.captain,
description: this.state.description,
atributes: this.state.atributes,
facebook: this.state.facebook,
instagram: this.state.instagram,
twitter: this.state.twitter,
youtube: this.state.youtube
};
this.props.addPlayer(playerData, this.props.history);
}
onImageChange(event) {
if (event.target.files && event.target.files[0]) {
this.setState({ avatarFile: event.target.files[0] });
}
}
render() {
return(
<div>
<form
onSubmit={this.onSubmit}
method="POST"
encType="multipart/form-data"
>
<div className="text-center mb-3">
<input
type="file"
name="file"
id="file"
accept="image/*"
className="inputfile"
onChange={this.onImageChange}
/>
<label htmlFor="file" className="btn btn-primary">
Elegir foto
</label>
</div>
</form>
</div>
);
}
}
AddPlayer.propTypes = {
addPlayer: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
profile: state.profile,
errors: state.errors
});
export default connect(
mapStateToProps,
{ addPlayer }
)(withRouter(AddPlayer));
AddPlayer动作
//Create player
export const addPlayer = (playerData, history) => dispatch => {
axios
.post("api/profile/player", playerData)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
它只是 post 播放器数据,然后重定向到另一个组件。
后端分为两个文件。 server.js 其中设置了所有中间件,profile.js 包含所有路由。
后端
server.js
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const passport = require("passport");
const cors = require("cors");
const users = require("./routes/api/users");
const profile = require("./routes/api/profile");
const matches = require("./routes/api/matches");
const app = express();
//body-parser middleware
app.use(bodyParser.urlencoded({ extended: false, limit: "50mb" }));
app.use(bodyParser.json({ limit: "50mb" }));
//db config
const db = require("./config/keys").mongoURI;
//cors
app.use(cors());
//connect to mongoose
mongoose
.connect(
db,
{ useNewUrlParser: true }
)
.then(() => console.log("MongoDB connected"))
.catch(err => console.log(err));
//Passport middleware
app.use(passport.initialize());
//Passport config
require("./config/passport")(passport);
app.use("/api/profile", profile);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`server running on port ${port}`));
profile.js
const express = require("express");
const router = express.Router();
const passport = require("passport");
const multer = require("multer");
const parser = multer({ dest: "./images" });
router.post(
"/player",
[passport.authenticate("jwt", { session: false }), parser.single("file")],
(req, res) => {
const newPlayer = {
name: req.body.name,
age: req.body.age,
apodo: req.body.apodo,
description: req.body.description,
captain: req.body.captain,
social: {
instagram: req.body.instagram,
facebook: req.body.facebook,
twitter: req.body.twitter,
youtube: req.body.youtube
}
};
//set the Avatar for the player
console.log(req.file);
});
如有任何帮助,我将不胜感激。谢谢。
您似乎有两个问题:
- 您没有使用正确的内容类型(
axios
默认情况下会假设 application/json
而您需要 multipart/form-data
)
- 您似乎假设上传会正常工作,因为它是表单的一部分,当您覆盖
onSubmit
并调用 e.preventDefault()
时,您取消了该表单的任何默认浏览器行为,需要手动将文件发送到服务器(我没有在您当前的代码中看到任何发生的事情)。
只需进行少量更改即可使此功能正常运行,第一步是将文件信息传递给您的操作。最简单的方法是在 file
字段中添加一个 ref
<input ref={c => this.img = c} ...
然后在onSubmit
函数中,你可以使用这个ref从DOM中获取Fileobject传递给action payload
onSubmit(e) {
e.preventDefault();
const playerData = {
...this.state,
file: this.img.files[0]
}
this.props.addPlayer(playerData, this.props.history);
}
在操作中,您需要将数据作为 multipart/form-data
请求发送,为此您只需将 FormData 传递给 axios
并让它处理设置适当的 headers等
export const addPlayer = (playerData, history) => dispatch => {
// build form data
const form = Object.keys(playerData).reduce((f, k) => {
f.append(k, playerData[k]);
return f;
}, new FormData());
// send request
return axios
.post("api/profile/player", form)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
您的服务器代码应该按原样工作。
我正在使用 MERN 堆栈,我希望能够在前端(反应)上传图像并在后端(express、nodejs)访问它以便稍后存储它。我正在使用 multer,但是当我尝试 console.log() req.file 对象时,我一直不确定。
前端:
import React, { Component } from "react";
import { Link, withRouter } from "react-router-dom";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { addPlayer } from "../../actions/profileActions";
class AddPlayer extends Component {
constructor(props) {
super(props);
this.state = {
avatarFile: null,
name: "",
age: "",
apodo: "",
captain: false,
description: "",
atributes: "",
facebook: "",
instagram: "",
twitter: "",
youtube: "",
};
this.onSubmit = this.onSubmit.bind(this);
this.onImageChange = this.onImageChange.bind(this);
}
onSubmit(e) {
e.preventDefault();
const playerData = {
name: this.state.name,
age: this.state.age,
apodo: this.state.apodo,
captain: this.state.captain,
description: this.state.description,
atributes: this.state.atributes,
facebook: this.state.facebook,
instagram: this.state.instagram,
twitter: this.state.twitter,
youtube: this.state.youtube
};
this.props.addPlayer(playerData, this.props.history);
}
onImageChange(event) {
if (event.target.files && event.target.files[0]) {
this.setState({ avatarFile: event.target.files[0] });
}
}
render() {
return(
<div>
<form
onSubmit={this.onSubmit}
method="POST"
encType="multipart/form-data"
>
<div className="text-center mb-3">
<input
type="file"
name="file"
id="file"
accept="image/*"
className="inputfile"
onChange={this.onImageChange}
/>
<label htmlFor="file" className="btn btn-primary">
Elegir foto
</label>
</div>
</form>
</div>
);
}
}
AddPlayer.propTypes = {
addPlayer: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired,
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
profile: state.profile,
errors: state.errors
});
export default connect(
mapStateToProps,
{ addPlayer }
)(withRouter(AddPlayer));
AddPlayer动作
//Create player
export const addPlayer = (playerData, history) => dispatch => {
axios
.post("api/profile/player", playerData)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
它只是 post 播放器数据,然后重定向到另一个组件。
后端分为两个文件。 server.js 其中设置了所有中间件,profile.js 包含所有路由。
后端 server.js
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const passport = require("passport");
const cors = require("cors");
const users = require("./routes/api/users");
const profile = require("./routes/api/profile");
const matches = require("./routes/api/matches");
const app = express();
//body-parser middleware
app.use(bodyParser.urlencoded({ extended: false, limit: "50mb" }));
app.use(bodyParser.json({ limit: "50mb" }));
//db config
const db = require("./config/keys").mongoURI;
//cors
app.use(cors());
//connect to mongoose
mongoose
.connect(
db,
{ useNewUrlParser: true }
)
.then(() => console.log("MongoDB connected"))
.catch(err => console.log(err));
//Passport middleware
app.use(passport.initialize());
//Passport config
require("./config/passport")(passport);
app.use("/api/profile", profile);
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`server running on port ${port}`));
profile.js
const express = require("express");
const router = express.Router();
const passport = require("passport");
const multer = require("multer");
const parser = multer({ dest: "./images" });
router.post(
"/player",
[passport.authenticate("jwt", { session: false }), parser.single("file")],
(req, res) => {
const newPlayer = {
name: req.body.name,
age: req.body.age,
apodo: req.body.apodo,
description: req.body.description,
captain: req.body.captain,
social: {
instagram: req.body.instagram,
facebook: req.body.facebook,
twitter: req.body.twitter,
youtube: req.body.youtube
}
};
//set the Avatar for the player
console.log(req.file);
});
如有任何帮助,我将不胜感激。谢谢。
您似乎有两个问题:
- 您没有使用正确的内容类型(
axios
默认情况下会假设application/json
而您需要multipart/form-data
) - 您似乎假设上传会正常工作,因为它是表单的一部分,当您覆盖
onSubmit
并调用e.preventDefault()
时,您取消了该表单的任何默认浏览器行为,需要手动将文件发送到服务器(我没有在您当前的代码中看到任何发生的事情)。
只需进行少量更改即可使此功能正常运行,第一步是将文件信息传递给您的操作。最简单的方法是在 file
字段中添加一个 ref
<input ref={c => this.img = c} ...
然后在onSubmit
函数中,你可以使用这个ref从DOM中获取Fileobject传递给action payload
onSubmit(e) {
e.preventDefault();
const playerData = {
...this.state,
file: this.img.files[0]
}
this.props.addPlayer(playerData, this.props.history);
}
在操作中,您需要将数据作为 multipart/form-data
请求发送,为此您只需将 FormData 传递给 axios
并让它处理设置适当的 headers等
export const addPlayer = (playerData, history) => dispatch => {
// build form data
const form = Object.keys(playerData).reduce((f, k) => {
f.append(k, playerData[k]);
return f;
}, new FormData());
// send request
return axios
.post("api/profile/player", form)
.then(res => history.push("/dashboard"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
您的服务器代码应该按原样工作。