I am trying to upload a file but I can't do it. I got a name for my file in database but I don't have the file in my file folder. When I am doing a console.log(req.file), I got an "undefined" I don't know what I'm missing I am using node.js, mySql (sequelize). React in front
app.js
const express = require('express');
const cookieParser = require('cookie-parser')
const app = express();
const path = require('path');
const userRoutes = require('./routes/user')
const postRoutes = require('./routes/post')
const commentRoutes = require('./routes/comment')
// no cors error
app.use((req, res, next) => {
const allowedOrigins = ['http://127.0.0.1:3000', 'http://localhost:3001', 'http://127.0.0.1:4200', 'http://localhost:4200'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
//res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:8020');
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', true);
return next();
});
// request access req.body
app.use(express.json());
app.use(cookieParser())
app.use('/images', express.static(path.join(__dirname, 'images')));
// app.use('/front/public/uploads/posts', express.static(path.join(__dirname, 'front/public/uploads/posts')));
// routes
app.use('/api/auth', userRoutes)
app.use('/api/post', postRoutes)
app.use('/api/comment', commentRoutes)
module.exports = app;
ROUTES
const express = require('express');
const router = express.Router();
const postCtrl = require('../controllers/post');
const auth = require('../middleware/auth')
const multer = require('../middleware/multer-config');
router.get('/', postCtrl.getAllPost);
router.post('/', auth, multer, postCtrl.createPost);
module.exports = router;
middleware/multer.js
const multer = require('multer');
// MIME types dictionnary
const MIME_TYPES = {
'image/jpg': 'jpg',
'image/jpeg': 'jpg',
'image/png': 'png'
};
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, 'images');
},
filename: (req, file, callback) => {
const name = file.originalname.split(' ').join('_');
const extension = MIME_TYPES[file.mimetype];
callback(null, name Date.now() '.' extension);
}
});
module.exports = multer({storage: storage}).single('image');
Post.js
const models = require('../models');
const jwt = require('jsonwebtoken');
// create a post
exports.createPost = async(req, res, next) => {
const token = req.cookies.jwt;
const decodedToken = jwt.verify(token, process.env.JWT_TOKEN);
const userId = decodedToken.userId;
console.log(req.file) // undefined
models.User.findOne({ where: { id: userId } })
.then((userFound) => {
if (userFound) {
if (req.file) {
const post = new models.Post({
UserId: userFound.id,
content: req.body.content,
imageUrl: `${req.protocol}://${req.get('host')}/images/${req.file.filename}`,
})
console.log(post)
post.save()
.then(() => res.status(201).json({ message: 'Post avec image créé !' }))
.catch(error => res.status(400).json({ error: 'Le post avec image n\'a pas pu être créé !' }));
}
else {
const post = new models.Post({
UserId: userFound.id,
content: req.body.content,
imageUrl: req.body.imageUrl,
})
console.log('post sans image')
console.log(post)
post.save()
.then(() => res.status(201).json({ message: 'Post sans image créé !' }))
.catch(error => res.status(400).json({ error }));
}
}
else {
return res.status(404).json({ error: 'Utilisateur non trouvé' })
}
})
};
front part (using react) postForm.js
import axios from 'axios';
import React, { useContext, useEffect, useState } from 'react';
import { NavLink } from 'react-router-dom';
import { UserContext } from '../../UserContext';
import { isEmpty } from './../Utils'
const NewPostForm = () => {
const uid = useContext(UserContext)
const [firstName, setFirstName] = useState('')
const [lastName, setLastName] = useState('')
const [message, setMessage] = useState('')
const [postPicture, setPostPicture] = useState('')
const [video, setVideo] = useState('')
const [file, setFile] = useState('')
useEffect(() => {
const getUserInfo = async () => {
if (uid !== null) {
const userId = uid.userId
await axios ({
method: "get",
url: `http://localhost:3000/api/auth/${userId}`,
withCredentials: true,
})
.then((res) => {
setFirstName(res.data.first_name)
setLastName(res.data.last_name)
})
.catch((err) => {
console.log(err)
})
}
}
getUserInfo()
}, [uid, firstName, lastName])
const handlePost = async () => {
if (message || postPicture || video) {
const data = new FormData()
// data.append("video", video)
data.append("UserId", uid.userId)
data.append("content", message)
if (file) data.append("imageUrl", file.name)
console.log(file)
axios({
method: "post",
baseURL: `http://localhost:3000/api/post`,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
},
data: data
})
.then((res) => {
console.log(res.data)
})
.catch((err) => {
console.log(err)
})
// cancelPost()
} else {
alert("Veuillez entrer un message")
}
}
const handlePicture = (e) => {
setPostPicture(URL.createObjectURL(e.target.files[0]))
setFile(e.target.files[0])
// setVideo('')
}
const cancelPost = () => {
setMessage('')
setPostPicture('')
setVideo('')
setFile('')
}
return (
<div className='post-container' style={{border: "1px solid black"}}>
<NavLink to="/profile">
<div className='user-info'>
<img src="uploads/profile.jpg" width="50px" alt="profil de l'utilisateur" />
<p>{firstName} {lastName} is connected</p>
</div>
</NavLink>
<div className='post-form'>
<textarea
type="text"
name="message"
id="message"
cols="50"
rows="5"
placeholder='Quoi de neuf ?'
onChange={(e) => setMessage(e.target.value)}
value={message}
></textarea>
<img src={postPicture} alt="" className="img-preview" width="200px" />
</div>
<div className='footer-form'>
<div className='icon'>
{isEmpty(video) && (
<>
<img src="" alt="icone paysage" />
<input
type="file"
id='file-upload'
name='file'
accept='.jpg, .jpeg, .png'
onChange={(e) => handlePicture(e)}
/>
</>
)}
{video && (
<button onClick={(e) => setVideo('')}>Supprimer vidéo</button>
)}
</div>
<div className='btn-send'>
{message || postPicture || video.length > 20 ? (
<button className='cancel' onClick={(e) => cancelPost()}>Annuler</button>
) : null}
<button className='send' onClick={(e) => handlePost()}>Envoyer</button>
</div>
</div>
</div>
);
};
export default NewPostForm;
CodePudding user response:
You need to send the actual file rather than just the name of the file. The server will not be able to access the file from a user's computer by its name.
To send a file in a post request, you also need to change the content type of the request to one that Multer expects.
const handlePost = async () => {
if (message || postPicture || video) {
const data = new FormData();
data.append("UserId", uid.userId);
data.append("content", message);
if (file) {
data.append("image", file);
}
try {
const res = await axios({
method: "post",
baseURL: `http://localhost:3000/api/post`,
withCredentials: true,
headers: { 'Content-Type': 'multipart/form-data' },
data: data,
});
console.log('File uploaded', res.data);
} catch (err) {
console.error('Failed to upload file', err);
}
} else {
alert("Veuillez entrer un message");
}
}
As a side note, you should either use async
/await
(with try
/catch
) or then
/catch
. They are just two different syntaxes for promises.