Search code examples
javascriptnode.jsmulter

req.file is undefined for multiple file uploads


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 
      }
  },
 }));

Solution

  • To properly populate req.files you need to use formdata.append like this:

    images.forEach(image => {
      formdata.append('images', image);
    })