Search code examples
node.jsreactjsaxiosmultipartform-datadropzone

Dropzone / React / NodeJS - Upload Image Array


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?


Solution

  • 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])
    }