node.jsmultipartform-datanode-requestmultiparty

Getting ERROR: uncaughtException: source.on is not a function, when using request and multiparty for multipart/form-data


I am trying to send data from my node application to a 3rd party HTTP endpoint.

I am parsing the data on the request object coming from the client using the multiparty module and sending the data through the request module. I am getting the error

error: uncaughtException: source.on is not a function

var request = require('request');
const multiparty = require('multiparty');

function addAttachment(req, res) {
  let form = new multiparty.Form();
  let parsedFile = {};
  const formData = {};
  form.parse(req, function(err, fields, files){
    Object.keys(fields).forEach(function(name) {
      formData[name] = fields[name][0];
    });

    Object.keys(files).forEach(function(name) {
      logger.debug(name);
      parsedFile[name] = files[name][0];
    });
    formData.uploadFile = parsedFile.uploadFile;
    logger.debug('formData ', formData);

    reqOptions.url = imageURL;
    reqOptions.formData = formData;

    logger.debug('REQ_OPTIONS ', reqOptions);

    request.post(reqOptions, function (err, response, body) {
      if (err) {
        logger.warn(req, ' Error sending attachment', err);
        res.status(400);
        res.json({ "msg": "Error sending attachment" });
      } else {
        res.status(201);
        logger.debug('BODY ', body);
        res.send(body);
      }
    });
  });
}

The reqOptions obj contains the headers, url, auth obj, we then add the form data to it.

When I log the form data it looks to be in the correct format

{
"meta": {
    "prop1": "xxxxxx",
    "prop2": "xxxxxxxxxxxxx",
    "uploadFile": {
        "fieldName": "uploadFile",
        "originalFilename": "test.PNG",
        "path": "/tmp/W1IppPiK04JpkPrnZWEhzkmV.PNG",
        "headers": {
            "content-disposition": "form-data; name=\"uploadFile\"; filename=\"test.PNG\"",
            "content-type": "image/png"
        },
        "size": 42786
    }
}

}


Solution

  • So after some hair pulling and digging around, I was able to post form data to the external API. I decide to change the node modules I was using to connect-multiparty. Connect will parse the request headers and decode the post-form-data allowing you to access the data from the req obj E.G req.body now have the added properties and req.files has uploaded files.

    const multipart = require('connect-multiparty');
    const multipartMiddleware = multipart();
    

    Then add the multipartMiddleware to the route.

     app.post('/api/addAttachment' multipartMiddleware, MyController.addAttachment);
    

    Then in my controller file I changed the code to use connect-multipart.

    const fs = require('fs');
    var request = require('request');  
    
    function addAttachment(req, res) {
      const TMP = '/tmp';
    
      let formData = {};
    
      Object.keys(req.body).forEach((propName) =>{
       if (typeof propName === 'string') {
        logger.debug(propName, ' is a string');
        formData[propName] = req.body[propName];
       } else {
        logger.debug(propName, ' is not a string')
       }
      });
      //The files get added to the tmp folder on the files system,
      //So we create a stream to read from tmp folder, 
      //at the end end we need to delete the file
      formData['uploadFile'] = fs.createReadStream(req.files.uploadFile.path);
      logger.debug('FORM DATA ', formData, '\n');
    
      reqOptions.url = imageUrl;
      reqOptions.headers = {'Content-Type': 'multipart/form-data','Accept': 'application/json'};
      reqOptions.formData = formData;
    
      logger.debug('REQ_OPTIONS ', reqOptions, '\n');
    
      request.post(reqOptions, function (err, response, body) {
        if (err) {
          removeFiles(TMP);
          logger.warn(req, ' Error sending attachment', err);
          res.status(400);
          res.json({"msg": "Error sending attachment"});
        } else {
          removeFiles(TMP);
          res.status(201);
          logger.debug('BODY ', body);
          res.send(body);
        }
      });
    }