TypeError: Cannot read property 'filename' of undefined 'getting this error while creating a User'

TypeError: Cannot read property 'filename' of undefined 'getting this error while creating a User'

我正在尝试创建一个带有头像的用户。我正在使用 multer,我不知道如何使用 multer 我只是按照文档操作,但是当我尝试使用个人资料图片创建用户时它显示错误 TypeError: Cannot read property 'filename' of undefined。我正在使用 MongoDB、express.js 作为后端。

这是我的代码

型号(user.js)

const mongoose = require('mongoose');
const bcrypt = require('bcrypt');

const userSchema = new mongoose.Schema({
    firstName: {
        type: String,
        required: true,
        trim: true,
        min: 3,
        max: 20
    },
    lastName: {
        type: String,
        required: true,
        trim: true,
        min: 3,
        max: 20
    },
    username: {
        type: String,
        required: true,
        trim: true,
        unique: true,
        index: true,
        lowercase: true
    },
    email: {
        type: String,
        required: true,
        trim: true,
        unique: true,
        lowercase: true
    },
    hash_password: {
        type: String,
        required: true
    },
    profilePicture: { type: String }
}, { timestamps: true });

userSchema.virtual('fullName').get(function () {
    return `${this.firstName} ${this.lastName}`
});

userSchema.methods = {
    authenticate: async function (password) {
        return await bcrypt.compare(password, this.hash_password);
    }
};

module.exports = mongoose.model('User', userSchema);

控制器(auth.js)

const User = require('../models/user');
const bcrypt = require('bcrypt');
const shortid = require('shortid');

exports.signup = async (req, res) => {
    User.findOne({ email: req.body.email }).exec(async (error, user) => {
        if (error) return res.status(400).json({ error })
        if (user)
            return res.status(400).json({
                message: "User already registered",
            });

        const { firstName, lastName, email, password } = req.body;
        const hash_password = await bcrypt.hash(password, 10);
        const profilePicture = req.file.filename

        const userSignUp = {
            firstName,
            lastName,
            email,
            hash_password,
            profilePicture: profilePicture,
            username: shortid.generate(),
        }

        const _user = new User(userSignUp);

        _user.save((error, data) => {
            if (error) {
                return res.status(400).json({
                    message: error,
                });
            }

            if (data) {
                return res.status(201).json({
                    message: "user created Successfully..!",
                });
            }
        });
    });
};

路由器(auth.js)

const express = require('express');
const { signup, signin, signout } = require('../controller/auth');
const { validateSignupRequest, isRequestValidated, validateSigninRequest } = require('../../validators/auth');
const router = express.Router();
const path = require('path');
const multer = require('multer');

router.use(express.static(__dirname+ '../../uploads'));

const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, '../../uploads')
    }, 
    filename: (req,file, cb) => {
        cb(null, file.filename + "_" + Date.now() + path.extname(file.originalname));
    }
});

const upload = multer({ storage: storage }).single('profilePicture');

router.post('/signup', validateSignupRequest,isRequestValidated,upload,signup);

module.exports = router

index.server.js

const express = require('express');
const mongoose = require('mongoose');
const env = require('dotenv');
const cors = require('cors');

const app = express(); 

env.config();

// connecting to mongoose database
mongoose.connect(process.env.DATABASE,
    {
    useNewUrlParser:true, 
    useUnifiedTopology:true,
    useCreateIndex: true,
    useFindAndModify: false
}).then(() => {
    console.log('database connected successfully')
}).catch((err) => {
    console.log(err)
});

// routes
const authRoutes = require('./Home_Search_Client/routes/auth');

// middlewares
app.use(cors());
app.use(express.json());
app.use('/api', authRoutes);


// PORT NUMBER 
app.listen(process.env.PORT, () => {
    console.log(`server is running at port: ${process.env.PORT}`)
});

前端 (React、Redux)

动作(user.action.js)

import axios from '../helpers/axios';
import { userContants } from './constants';

export const signup = (user) => {
    console.log(user)
    return async (dispatch) => {

        dispatch({ type: userContants.USER_REGISTER_REQUEST });
        const res = await axios.post(`/signup`, {
            ...user
        });

        if (res.status === 201) {
            const { message } = res.data;
            dispatch({
                type: userContants.USER_REGISTER_SUCCESS,
                payload: { message }
            });
        } else {
            if (res.status === 400) {
                dispatch({
                    type: userContants.USER_REGISTER_FAILURE,
                    payload: { error: res.data.error }
                });
            }
        }
    }
};

减速机(user.reducer.js)

import { userContants } from "../actions/constants"

const initState = {
    error: null,
    message: '',
    loading: false
}

export default (state = initState, action) => {
    switch (action.type) {
        case userContants.USER_REGISTER_REQUEST:
            state = {
                ...state,
                loading: true
            }
            break;
        case userContants.USER_REGISTER_SUCCESS:
            state = {
                ...state,
                loading: false,
                message: action.payload.message
            }
            break;
        case userContants.USER_REGISTER_FAILURE:
            state = {
                ...state,
                loading: false,
                error: action.payload.error
            }
            break;
    }

    return state;
}

容器(Signup.js)

import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Button, Col, Container, Form, Row } from 'react-bootstrap'
import Layout from '../../Components/Layout'
import Input from '../../Components/UI/Input'
import { signup } from '../../actions'
import { Redirect } from 'react-router-dom';

const Signup = (props) => {
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const [profilePicture, setProfilePicture] = useState('');
    const auth = useSelector((state) => state.auth)
    const user = useSelector((state) => state.user)
    const dispatch = useDispatch();

    useEffect(() => {
        if(!user.loading){
            setFirstName('');
            setLastName('');
            setEmail('');
            setPassword('');
            setProfilePicture('')
        }
    }, [user.loading]);

    const userSignup = (e) => {
        e.preventDefault();
        const user = {
            firstName,
            lastName,
            email,
            password,
            profilePicture
        };
        dispatch(signup(user));
        console.log(user)
    };

    if(auth.authenticate) {
        return <Redirect to={'/'} />
    }

    if(user.loading) {
        return <p>Loading...</p>
    }

    const handleProfilePicture = (e) => {
        setProfilePicture(e.target.files[0]);
    }

    return (
        <Layout>
            <Container>
                {user.message}
                <Row>
                    <Col md={{ span:6, offset:3 }}>
                        <Form onSubmit={userSignup}>
                            <Row>
                                <Col md = {6}>
                                    <Input 
                                        label = 'First Name'
                                        placeholder='First Name'
                                        value= {firstName}
                                        type='text'
                                        onChange={(e) => setFirstName(e.target.value)}
                                    />
                                </Col>
                                <Col md = {6}>
                                    <Input 
                                        label = 'Last Name'
                                        placeholder='Last Name'
                                        value= {lastName}
                                        type='text'
                                        onChange={(e) => setLastName(e.target.value)}
                                    />
                                </Col>
                            </Row>
                            
                            <Input
                                label='Email'
                                placeholder='Email'
                                value={email}
                                type='email'
                                onChange={(e) => setEmail(e.target.value)}
                            />
                            <Input
                                label='Password'
                                placeholder='Password'
                                value={password}
                                type='password'
                                onChange={(e) => setPassword(e.target.value)}
                            />

                            
                            <input type="file" name= 'profilePicture' onChange={handleProfilePicture} />

                            <Button variant='primary' type='submit' >
                                Submit
                            </Button>

                        </Form>
                    </Col>
                </Row>
            </Container>
        </Layout>
    )
}

export default Signup

要从前端发送文件和其他数据,您需要使用 FormData API。使用 user 对象和 axios,我会做这样的事情:

const formData = new FormData();

// Append all properties of the `user` object to the form
for (const [key, value] of Object.entries(user)) {
  formData.append(key, value);
}

const response = await axios.post('/signup', formData, {
  headers: {
    // Multer only parses "multipart/form-data" requests
    'Content-Type': 'multipart/form-data',
  },
});