0

I'm trying to upload an array of photos to a server but the req.files array always shows up empty when it gets there.

req.body displays the array as expected.

Terminal console.log output for /upload POST request

The images are added through a Dropzone component. (I've tried switching this for a standard input but they both seem to pass files the same way)

    <Dropzone
       onDrop={onDrop}
       onSubmit={uploadPhotos}
       maxFiles={20}
       inputContent="Drop 20 Images"
       inputWithFilesContent={files => `${20 - files.length} more`}
     />

The images are applied to FormData with the name image files are appended before being sent via an Axios POST request with multipart/form-data headers set.

export const uploadPhotos = (files) => {
const formData = new FormData();
for (let i = 0; i < files.length; i += 1) {
  formData.append("image[]", files[i]);
}
const config = {
  headers: {
    'Content-Type': `multipart/form-data`
  }
}

return async (dispatch, getState) => {
    try {
      const response = await axios.post('/api/kite/upload',  
      formData, config)
      .then(function(response) {
        console.log(response.data);
        dispatch({
          type: ORDER_CHANGE,
          payload: response.data
        });
      });
    } catch (err) {
      console.log(err);
    } finally {
      console.log('done');
    }
  }
}

once passed to the server only req.body seems to contain any data and req.files is empty despite using Multer middleware as the second parameter. Once passed to files.map() items are undefined undefined, presumably because req.files is an empty array.

var multer  = require('multer');
var AWS = require('aws-sdk');
AWS.config.setPromisesDependency(bluebird);

const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'upload')
    },
    filename: (req, file, cb) => {
        cb(null, file.fieldname + '-' + Date.now())
    }
});
const upload = multer({
    storage: storage
}).array('image');

router.post('/upload', upload, function (req, res) {
  const file = req.files;
  let s3bucket = new AWS.S3({
    accessKeyId: IAM_USER_KEY,
    secretAccessKey: IAM_USER_SECRET,
    Bucket: 'BUCKETNAME'
  });

  s3bucket.createBucket(function () {
      let Bucket_Path = 'https://console.aws.amazon.com/s3/buckets/BUCKETNAME?region=eu-west-1';
      var ResponseData = [];

  file.map((item) => {
     // item.x are all undefined
      var fileStream = fs.createReadStream(filePath);
      var params = {
        Bucket: Bucket_Path,
        Key: item.originalname,
        Body: item.buffer,
        ACL: 'public-read'
    };

  s3bucket.upload(params, function (err, data) {
        if (err) {
         res.json({ "error": true, "Message": err});
        } else{
            ResponseData.push(data);
            if(ResponseData.length == file.length){
              res.json({ "error": false, "Message": "File Uploaded    SuceesFully", Data: ResponseData});
            }
          }
       });
     });
   });
});

My end goal is to pass the images to an Amazon S3 bucket. I don't think it impacts this since there is no data to send but I've included it incase it has somehow affecting this.

I've been through lots of other similar Stack Overflow questions and medium post and the main three resolutions to this issue seem to be included in the flow above.

  1. Append file name to items of FormData array
  2. Set POST request headers
  3. Include Multer middleware in express parameter

Can anyone help me figure out why req.files is an empty array?

wtmcm
  • 307
  • 1
  • 4
  • 19
  • 1
    I believe when using `FormData` to upload multiple files You should use `formData.append("image[]", files[i]);`. Have You tried that, with the `[]` at the end of the key? – Davit May 10 '20 at 17:54
  • I gave it a try but it looks like req.files is still empty. I'll update the answer to include this. Thanks. – wtmcm May 10 '20 at 18:07

1 Answers1

1

It might be that Dropzone isn't processing the files. Try adding this to the uploadPhotos function:

const acceptedFiles = myDropzone.getAcceptedFiles() // "myDropzone" is just the Dropzone instance
for (let i = 0; i < acceptedFiles.length; i++) {
   myDropzone.processFile(acceptedFiles[i])
}
Ludolfyn
  • 1,806
  • 14
  • 20
  • That's exactly what it was. Thanks for the help. – wtmcm May 11 '20 at 17:30
  • Yaaas @wtmcm! I'm glad it worked man! I struggled my ass off with the exact same thing. Dropzone can be tricky. I've realised that if something isn't working, then I'm probably missing some Dropzone method. There's another issue you might run into when uploading files for a second time if the page hasn't reloaded. You need to remove all the attached files after upload with `myDropzone.removeAllFiles()`, otherwise they'll upload again next time. I wrote a massive answer about how I do my Dropzone things—if you run into more issues then maybe there's something in there that can help. – Ludolfyn May 11 '20 at 17:45
  • https://stackoverflow.com/questions/60856642/how-to-get-dropzone-js-in-combination-with-multiple-input-and-select-fields-work/60863641#60863641 – Ludolfyn May 11 '20 at 17:45
  • 1
    Thanks a million. I was really struggling with this one as I haven't uploaded media files to a server before. I'll keep this in mind when I inevitably run into the next issue! – wtmcm May 11 '20 at 19:14