I've read through all the possible answers here but I'm not sure anymore what the problem could be because none of them works. I have one form that uploads a single image and it works as expected but on a different form where I try and upload multiple files and have form inputs, the req.file is always undefined and the image data ends up in the req.body. So somewhere I think maybe there is an issue where multer is not getting the file data...?
I read in a couple of places that body-parser is possibly not playing well with multer but I unfortunately need them both.
route
const destination = './public/uploads/posts';
const storage = multer.diskStorage({
destination: destination,
filename: (req, file, cb) => {
cb(null, file.originalname);
},
});
const upload = multer({storage}).array('images');
router.post('/posts', (req, res) => {
upload(req, res, (err) => {
if(err) {
console.log('errors', err)
res.json({success: false, error: err})
} else {
if(req.files === undefined) {
console.log('error: Images returned undefined');
} else {
const { title, description, url, auth_name, auth_id } = req.body;
if(!title || !description) {
return res.json({
success: false,
error: 'All fields required',
message: 'Title and description fields are required'
});
}
// path we store in the db
let imageLocation = [];
const post = new Post();
for (const file of req.files) {
imageLocation.concat('/uploads/posts/' + file.filename)
}
post.title = title;
post.description = description;
post.url = url;
post.author_name = auth_name;
post.author = auth_id;
post.images = imageLocation;
post.save((err, post) => {
if(err) {
return res.json({ success: false, error: err, message: 'post failed' });
} else {
return res.json({ success: true, message: "added new post", post: post });
}
});
}
}
});
});
post in react component
My images are set in state which I get from the DropZone package (I tested without DropZone as well with same results. I made sure the the input name is "images"
submitNewPost = () => {
const { title, description, url, images } = this.state;
const auth_id = this.props.author_id;
const auth_name = this.props.author_name;
const formdata = new FormData();
formdata.append('title', title);
formdata.append('description', description);
formdata.append('url', url);
formdata.append('author_name', auth_name);
formdata.append('author', auth_id);
formdata.append('images', images);
fetch('/api/posts', {
method: 'POST',
body: formdata
}).then(res => res.json())
.then((res) => {
if (!res.success) {
this.setState({ message: res.message });
} else {
this.setState({
title: '',
description: '',
url: '',
images: '',
message: res.message
});
socket.emit('newPost', res.post);
}
});
}
UPDATE
With some help on this thread I managed to upload the images using my existing route and postman. When I test the route using my form, I still get an empty req.file and no uploads.
I added express-multipart-file-parser to my setup and I figure that is what brought me one step closer to fixing this.
Server
This is what I added to my server.js
import { fileParser } from 'express-multipart-file-parser';
app.use(fileParser({
rawBodyOptions: {
limit: '15mb', //file size limit
},
busboyOptions: {
limits: {
fields: 20 //Number text fields allowed
}
},
}));
To properly populate req.files
you need to use formdata.append
like this:
images.forEach(image => {
formdata.append('images', image);
})