Очень странно, что все учебники, которые я нашел в Интернете, показывают, как загрузить изображение, но не показывают, как это сделать с другими включенными текстовыми вводами.
Таким образом, я столкнулся с проблемой, пытаясь загрузить изображения, а также другие текстовые данные в той же форме. Потратил часы на поиск в SO и Google, но не смог найти ничего подходящего для моей ситуации.
Я использую React & Redux и пакет express-fileupload для загрузки файлов.
В любом случаевот что я пробовал:
Backend
campgroundRoutes.js
const express = require('express');
const router = express.Router();
const fileUpload = require('express-fileupload');
router.use(fileUpload());
const Campground = require(`../../models/campground`);
const checkAuth = require('../../middleware/check-auth');
router.post('/', checkAuth, (req, res, next) => {
const file = req.files.file;
console.log('req.files: ', req.files); // req.files is undefined
uploadPath = './assets/uploadedImages/' + file.name;
file.mv(uploadPath, err => {
if (err) {
console.error('Error: ', err);
return res
.status(500)
.json({ error: err, message: 'Failed to upload file' });
}
res.json({ fileName: file.name });
});
const campgroundToPost = new Campground({
title: req.body.title,
description: req.body.description,
cost: req.body.cost,
imageName: file.name,
_author: {
id: req.userData.userId,
firstName: req.userData.firstName
}
});
campgroundToPost
.save()
.then(result => res.status(200).json({ campground: result }))
.catch(err => res.status(400).send(`Failed to add campground, ${err}`));
});
Внешний интерфейс
addCampground.js
import React, { Component } from 'react';
import TextField from '@material-ui/core/TextField';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import '../../styles/addCampground.css';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import {
actionAddCampground,
getCampgroundDetails
} from './actions/campgroundActions';
class AddCampground extends Component {
constructor(props) {
super(props);
this.state = {
title: '',
description: '',
cost: '',
selectedImage: null
};
}
handleChange = e => {
const { name, value } = e.target;
this.setState({
[name]: value
});
};
uploadImage = e => {
this.setState(
{
selectedImage: e.target.files[0]
},
() => console.log(this.state.selectedImage)
);
};
addCampground = () => {
const title = this.state.title;
const description = this.state.description;
const cost = this.state.cost;
const data = new FormData();
data.append('file', this.state.selectedImage);
this.props
.actionAddCampground({
title,
description,
cost,
data
})
.then(res => console.log(res))
.catch(err => console.log('Error: ', err));
this.props.history.push('/home');
};
render() {
return (
<Card className="add-campground-card">
<CardContent>
<Typography
style={{ fontWeight: '400' }}
className="text-center"
variant="h6"
component="h6">
Add Your Campground
</Typography>
</CardContent>
<TextField
autoComplete="off"
name="title"
className="textfield"
label="Campground name"
variant="outlined"
value={this.state.title}
onChange={e => this.handleChange(e)}
/>
<TextField
autoComplete="off"
name="description"
className="textfield"
label="Campground description"
variant="outlined"
value={this.state.description}
onChange={e => this.handleChange(e)}
/>
<TextField
autoComplete="off"
name="cost"
className="textfield"
type="number"
label="Campground cost"
variant="outlined"
value={this.state.cost}
onChange={e => this.handleChange(e)}
/>
<input onChange={this.uploadImage} type="file" name="file" />
<Button
className="add-campground"
variant="contained"
color="primary"
onClick={this.addCampground}>
Add Campground
</Button>
</Card>
);
}
}
const mapStateToProps = state => {
return {
campground: state.campgroundList.singleCampground
};
};
const mapDispatchToProps = dispatch => {
return bindActionCreators(
{
actionAddCampground,
getCampgroundDetails
},
dispatch
);
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(AddCampground);
campgroundActions.js
// Only including add campground action here as only that's relevant
import {
ADD_CAMPGROUND,
} from '../actionTypes/types';
import axios from 'axios';
import { authHeader } from '../../../helpers/auth-header';
const API_URL = `http://localhost:5000/api`;
export const actionAddCampground = campground => {
return async dispatch => {
try {
const res = await axios.post(`${API_URL}/campgrounds`, campground, {
headers: authHeader()
});
dispatch({
type: ADD_CAMPGROUND,
payload: res
});
return res.data;
} catch (err) {
return console.log(err);
}
};
};
authHeader.js
export const authHeader = () => {
let user = JSON.parse(localStorage.getItem('user'));
let token = localStorage.getItem('token');
if (user && token) {
return {
Authorization: token
};
} else {
return {};
}
};
Это ошибка, которую я получаю в бэкэнде:
TypeError: Cannot read property 'file' of undefined
Я не могувыяснить, где я иду не так.