Итак, после того, как я сделаю POST-запрос для загрузки изображения для пользователя, изображение не отображается, а alt, я не знаю, что вызывает это, так как я не получаю никакой ошибки, и кажется, что работать, только то, что отображает alt изображения, но не само изображение. Вот некоторые из моих кодов, а также ссылка на репозиторий github.
https://github.com/tigerabrodi/ELance
Действие пользователя
export const updateUserImage = (image) => async dispatch => {
const formData = new FormData();
formData.append("image", image)
const config = {
headers: {
"Content-Type": "multipart/form-data"
}
}
try {
const res = await axios.post("/api/users/avatar", formData, config);
dispatch({
type: UserActionTypes.USER_IMAGE_UPDATED,
payload: res.data
});
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach(error => dispatch(setAlert(error.msg, 'danger')));
}
dispatch({
type: UserActionTypes.USER_ERROR
});
}
}
пользовательский редуктор
import {UserActionTypes} from "./user.types";
const initialState = {
token: localStorage.getItem("token"),
isAuthenticated: null,
loading: true,
user: null
}
const userReducer = (state = initialState, action) => {
const {type, payload} = action;
switch (type) {
case UserActionTypes.USER_LOADED:
case UserActionTypes.USER_IMAGE_UPDATED:
return {
...state,
isAuthenticated: true,
loading: false,
user: payload
};
case UserActionTypes.REGISTER_SUCCESS:
case UserActionTypes.LOGIN_SUCCESS:
localStorage.setItem('token', payload.token);
return {
...state,
...payload,
isAuthenticated: true,
loading: false
};
case UserActionTypes.REGISTER_FAIL:
case UserActionTypes.AUTH_ERROR:
case UserActionTypes.LOGIN_FAIL:
case UserActionTypes.LOGOUT:
case UserActionTypes.ACCOUNT_DELETED:
case UserActionTypes.USER_ERROR:
localStorage.removeItem('token');
return {
...state,
token: null,
isAuthenticated: false,
loading: false
};
default:
return state;
}
}
export default userReducer
Панель инструментов js (куда загружается изображение)
import React, {useEffect, Fragment, useState} from 'react';
import {connect} from "react-redux";
import {getCurrentProfile, deleteAccount} from "../../redux/profile/profile.actions";
import {Link} from "react-router-dom"
import Spinner from "../layout/Spinner";
import DashboardActions from "./DashboardActions"
import Experience from './Experience';
import Education from './Education';
import defaultUserImage from "../../assets/default-user-icon.jpg";
import {updateUserImage} from "../../redux/user/user.actions";
const Dashboard = ({
deleteAccount,
getCurrentProfile,
updateUserImage,
auth: {
user
},
profile: {
profile,
loading
}
}) => {
useEffect(() => {
getCurrentProfile();
}, [getCurrentProfile]);
const [file, setFile] = useState('');
const [imageFile, setImageFile] = useState(false);
const onChange = e => {
setFile(e.target.files[0]);
setImageFile(!imageFile);
};
const onSubmit = e => {
e.preventDefault();
updateUserImage(file);
}
return loading && profile === null
? <Spinner/>
: <Fragment>
<div className="container">
<div className="row">
<div className="col text-center">
<h1 className="display-1 text-warning">Dashboard</h1>
<p className="lead text-info">
<i className="fas fa-user"/>
Welcome {user && user.name}
</p>
<div className="col sm-5">
<img src={user.avatar ? user.avatar : defaultUserImage} alt="avatar" className="border border-success rounded-circle m-2" />
</div>
<form onSubmit={onSubmit}>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">Image</span>
</div>
<div class="custom-file">
<input type="file" onChange={e => onChange(e)} class="custom-file-input bg-info" />
<label class="custom-file-label">Upload Image</label>
</div>
</div>
{imageFile && (
<input type="submit" value="Submit Upload" className="btn btn-info btn-block m-2" />
)}
</form>
</div>
</div>
</div>
{profile !== null
? (
<Fragment>
<DashboardActions/>
<Experience experience={profile.experience}/>
<Education education={profile.education}/>
<div className="container">
<div className="row">
<div className="col text-center">
<div className="my-2">
<button className="btn-danger" onClick={() => deleteAccount()}>
<i className="fas fa-user-minus text-dark"/>
Delete My Account
</button>
</div>
</div>
</div>
</div>
</Fragment>
)
: (
<Fragment>
<div className="container">
<div className="row">
<div className="col text-center">
<p>You have not yet setup a profile, please add some info</p>
<Link to='/create-profile' className='btn btn-info my-1'>
Create Profile
</Link>
</div>
</div>
</div>
</Fragment>
)}
</Fragment>
}
const mapStateToProps = state => ({auth: state.auth, profile: state.profile})
export default connect(mapStateToProps, {getCurrentProfile, deleteAccount, updateUserImage})(Dashboard);
серверный маршрут пользователя
// @route POST api/users/avatar
// @desc Update Users Avatar
// @access Private
router.post("/avatar", auth, async (req, res) => {
try {
let user = await User.findById(req.user.id);
if (!user) {
return res.status(404).json({msg: "User was not found"})
}
if (!req.file) {
return res.status(400).json({msg: "Please upload a file"});
}
const imageUrl = req.file.path.replace("\\" ,"/");;
user = await User.findByIdAndUpdate(req.user.id, {avatar: imageUrl});
await user.save();
res.status(200).json(user);
} catch (err) {
console.error(err.message);
return res.status(500).json({msg: "Server error"});
}
})
сервер. js
const express = require('express');
const connectDB = require('./config/db');
const uuidv4 = require("uuid/v4")
const colors = require("colors");
const morgan = require("morgan");
const multer = require("multer");
const path = require("path");
const app = express();
const PORT = process.env.PORT || 9065;
// Connect Database
connectDB();
// Configuring Multer storage
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'images');
},
filename: (req, file, cb) => {
cb(null, uuidv4() + "_" + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
if (
file.mimetype === 'image/png' ||
file.mimetype === 'image/jpg' ||
file.mimetype === 'image/jpeg'
) {
cb(null, true);
} else {
cb(null, false);
}
};
// Init Middleware
app.use(express.json({ extended: false }));
// Use Multer
app.use(
multer({ storage: fileStorage, fileFilter: fileFilter }).single('image')
);
// Dev logging middleware
if (process.env.NODE_ENV === "development") {
app.use(morgan("dev"));
}
// Define Routes
app.use('/api/users', require('./routes/api/users'));
app.use('/api/auth', require('./routes/api/auth'));
app.use('/api/profile', require('./routes/api/profile'));
app.use('/api/posts', require('./routes/api/posts'));
// Serve static assets in production
if (process.env.NODE_ENV === 'production') {
// Set static folder
app.use(express.static('client/build'));
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
app.listen(PORT, () => console.log(`Server started on port ${PORT}`.cyan.underline.bold));