2

I'm trying to send a post request from my node server to an api that expects a file and other form data as an multipart/form-data.

Here is what my code looks like

var importResponse = function(csv){
  stringify(csv, function(err, output){
    request.post({
      headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
      url: url,
      formData: {
        surveyId: surveyId,
        file: {
            value: output,
            options: {
                fileName: 'test.csv',
                contentType:'text/csv'
            }
        }
      }
    }, function(error, response, body){
      console.log(body);
    });
  });
}

Using request-debug here is the request:

request:
   { debugId: 1,
     uri: 'https://co1.qualtrics.com/API/v3/responseimports',
     method: 'POST',
     headers:
      { 'X-API-TOKEN': 'removed',
        'content-type':
         'multipart/form-data; boundary=--------------------------010815605562947295265820',
        host: 'co1.qualtrics.com',
        'content-length': 575 } } }

and the response:

response:
   { debugId: 1,
     headers:
      { 'content-type': 'application/json',
        'content-length': '188',
        'x-edgeconnect-midmile-rtt': '28',
        'x-edgeconnect-origin-mex-latency': '56',
        date: 'Wed, 18 Jul 2018 03:57:59 GMT',
        connection: 'close',
        'set-cookie': [Array],
        'strict-transport-security': 'max-age=31536000; includeSubDomains; preload' },
     statusCode: 400,
     body:
      '{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Missing Content-Type for file part. name=file","errorCode":"MFDP_3"},"requestId":"322a16db-97f4-49e5-bf10-2ecd7665972e"}}' } }

The error I'm getting is: Missing Content-Type for file part.

I've added this in the options:

options: {
    fileName: 'test.csv',
    contentType:'text/csv'
}

When I look at the request, it seems as though the form data isn't included. But perhaps that is just the request-debug not showing it.

I saw a similar SO question and the answer was to use JSON.stringify. I tried changing my code to the following:

request.post({
  headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
  url: url,
  body: JSON.stringify({
    surveyId: surveyId,
    file: {
        value: output,
        options: {
            fileName: 'test.csv',
            contentType:'text/csv'
        }
    }
  })

However, I got the following error:

{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Missing boundary header"}}}

What am I doing wrong?

UPDATE When I tried changing the file value to a csv on my computer fs.createReadStream('test.csv'), it worked fine

    file: {
        value: fs.createReadStream('test.csv'),
        options: {
            contentType: 'text/csv'
        }
    }

So I assume there is something wrong with the way I'm giving the file. The output variable that I'm using as the file just looks like "QID1,QID2\nQID1,QID2\n1,2". I assume this is causing the problems, even though the error is a bit misleading. I tried creating a Readable that I found as a StackOverFlow answer like so:

var s = new Readable
s.push(output)
s.push(null) 

However, this lead to a Unexpected end of input

{"meta":{"httpStatus":"400 - Bad Request","error":{"errorMessage":"Unexpected end of input"}}}
Eric
  • 2,008
  • 3
  • 18
  • 31

4 Answers4

2

I found the issue. My first solution was fine, but instead of fileName it should have been filename

var importResponse = function(csv){
  stringify(csv, function(err, output){
    request.post({
      headers: {'X-API-TOKEN':token, 'content-type' : 'multipart/form-data'},
      url: url,
      formData: {
        surveyId: surveyId,
        file: {
            value: output,
            options: {
                filename: 'test.csv', //filename NOT fileName
                contentType:'text/csv'
            }
        }
      }
    }, function(error, response, body){
      console.log(body);
    });
  });
}
Eric
  • 2,008
  • 3
  • 18
  • 31
1

Is it possible that you are using the incorrect property name for your file?

A quick read of the forms info for the request Node module makes me think you should be using custom_file instead of file.

You can read more about it here: https://github.com/request/request#forms

1

Hey Eric check in which format they accept multipartRequest ,as I did for uploading the file on drive like this:

        request(options, function (err, response) {
            var boundary = '-------314159265358979323846';
            var delimiter = "\r\n--" + boundary + "\r\n";
            var close_delim = "\r\n--" + boundary + "--";

            var fileContent = 'Sample upload :)';

            var metadata = {
                'name': 'myFile.txt',
                'mimeType': 'text/plain\r\n\r\n'
            };

            var multipartRequestBody = delimiter + 'Content-Type: application/json\r\n\r\n' + JSON.stringify(metadata) + delimiter + 'Content-Type: ' + 'text/plain\r\n\r\n' + fileContent + close_delim;

            request(options, function (err, response) {
                var url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&access_token=' + JSON.parse(response.body).access_token;
                var options = {
                    method: 'POST',
                    url: url,
                    headers: {
                        'Content-Type': 'multipart/related; boundary="' + boundary + '"'
                    },
                    body: multipartRequestBody
                };

                request(options, function (err, response) {
                    res.send({resultdata: response.body});
                });
            });
        });

Set the multi-part as per your endpoint accepting.

Mohammad Raheem
  • 1,131
  • 1
  • 13
  • 23
  • Thanks, for the reply. I posted an update as to what I believe the issue is. I believe I need to create a `Readable` instead of supplying just a String. – Eric Jul 18 '18 at 05:16
0

Using frameworks like express(they automatically parse headers and response) and npm modules like multer for handling multipart form data helps cause they do all the heavy lifting for you

Sachin S
  • 186
  • 8