Skip to content

Instantly share code, notes, and snippets.

@tanaikech
Last active April 23, 2024 22:31
Show Gist options
  • Star 28 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save tanaikech/40c9284e91d209356395b43022ffc5cc to your computer and use it in GitHub Desktop.
Save tanaikech/40c9284e91d209356395b43022ffc5cc to your computer and use it in GitHub Desktop.
Multipart-POST Request Using Node.js

Multipart-POST Request Using Node.js

Here, I introduce 2 scripts for uploading files to Slack using Node.js as samples. These 2 sample scripts are for uploading files to Slack.

Sample script 1:

  • You can upload the zip file by converting byte array as follows.
    • At first, it builds form-data.
    • Adds the zip file converted to byte array and boundary using Buffer.concat().
    • This is used as body in request.

Basically, this is almost the same to the method using GAS.

var fs = require('fs');
var request = require('request');
var upfile = 'sample.zip';
fs.readFile(upfile, function(err, content){
    if(err){
        console.error(err);
    }
    var metadata = {
        token: "### access token ###",
        channels: "sample",
        filename: "samplefilename",
        title: "sampletitle",
    };
    var url = "https://slack.com/api/files.upload";
    var boundary = "xxxxxxxxxx";
    var data = "";
    for(var i in metadata) {
        if ({}.hasOwnProperty.call(metadata, i)) {
            data += "--" + boundary + "\r\n";
            data += "Content-Disposition: form-data; name=\"" + i + "\"; \r\n\r\n" + metadata[i] + "\r\n";
        }
    };
    data += "--" + boundary + "\r\n";
    data += "Content-Disposition: form-data; name=\"file\"; filename=\"" + upfile + "\"\r\n";
    data += "Content-Type:application/octet-stream\r\n\r\n";
    var payload = Buffer.concat([
            Buffer.from(data, "utf8"),
            new Buffer(content, 'binary'),
            Buffer.from("\r\n--" + boundary + "--\r\n", "utf8"),
    ]);
    var options = {
        method: 'post',
        url: url,
        headers: {"Content-Type": "multipart/form-data; boundary=" + boundary},
        body: payload,
    };
    request(options, function(error, response, body) {
        console.log(body);
    });
});

Sample script 2:

  • fs.createReadStream() can be used as a file for uploading to Slack.
var fs = require('fs');
var request = require('request');
request.post({
    url: 'https://slack.com/api/files.upload',
    formData: {
        file: fs.createReadStream('sample.zip'),
        token: '### access token ###',
        filetype: 'zip',
        filename: 'samplefilename',
        channels: 'sample',
        title: 'sampletitle',
    },
}, function(error, response, body) {
    console.log(body);
});

Result :

Both sample 1 and sample 2 can be uploaded zip file to Slack as follows. For both, even if filetype is not defined, the uploaded file is used automatically as a zip file.

@katio
Copy link

katio commented Dec 2, 2018

It works like charm. Thx. I just want to mention that Request is a external package: https://github.com/request/request#readme
So first:
npm install --save request

@abhishek200593
Copy link

Hey I am confused about one thing, the boundary always be changes with each request, so how is it handled here?

@iaravindreddyp
Copy link

Hey I am confused about one thing, the boundary always be changes with each request, so how is it handled here?

i guess it doesn't necessarily have to be different every-time you make a request

@Bneji92
Copy link

Bneji92 commented Feb 26, 2020

keep getting this error:

_http_outgoing.js:618 throw new ERR_INVALID_ARG_TYPE('first argument', ^

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string or Buffer. Received type object at write_ (_http_outgoing.js:618:11) at ClientRequest.write (_http_outgoing.js:586:15) at Request.write (C:\Users\Benja\Desktop\Repo\Manager\node_modules\request\request.js:1500:27) at end (C:\Users\Benja\Desktop\Repo\Manager\node_modules\request\request.js:549:18) at Immediate._onImmediate (C:\Users\Benja\Desktop\Repo\Manager\node_modules\request\request.js:578:7) at processImmediate (internal/timers.js:439:21) { code: 'ERR_INVALID_ARG_TYPE' }

@ade-akinyede
Copy link

This helped me especially with solving a JWT payload hashing challenge - thank you!

@rakeshnambiar
Copy link

@tanaikech I am trying to make a POST request for curl -X POST "http://myserver-ip-address:9090/api/result" -H "accept: */*" -H "Content-Type: multipart/form-data" -F "allureResults=@allure-results.zip;type=application/x-zip-compressed and the code looks like:

request.post({
    url: 'http://myserver-ip-address:9090/api/report',
    headers: {
        "Content-Type": "multipart/form-data",
        "accept": "*/*"
    },
    formData: {
        file: fs.createReadStream('allure-results.zip'),
        filetype: 'application/x-zip-compressed',
        filename: 'samplefilename',
        channels: 'sample',
        title: 'sampletitle',
    },
}, function(error, response, body) {
    console.log(body);
});

But I am getting the response undefined and I am not sure how to pass allureResults=@allure-results.zip. Please help.

@ccondry
Copy link

ccondry commented Feb 22, 2021

thank you for this code. I was struggling to get node-fetch to work with form-data if I was building a stream from a string instead of from a real file with fs.createReadStream. This helped me build the headers and body manually for a multipart file upload and it finally works.

@ridhuanhassan
Copy link

thank you a lot. I was able to send plain text data with form-data, but I was struggling to send binary data properly until I read this line.

var payload = Buffer.concat([
        Buffer.from(data, "utf8"),
        new Buffer(content, 'binary'),
        Buffer.from("\r\n--" + boundary + "--\r\n", "utf8"),
]);

@spyinfo
Copy link

spyinfo commented Aug 27, 2021

@tanaikech I am trying to make a POST request for curl -X POST "http://myserver-ip-address:9090/api/result" -H "accept: */*" -H "Content-Type: multipart/form-data" -F "allureResults=@allure-results.zip;type=application/x-zip-compressed and the code looks like:

request.post({
    url: 'http://myserver-ip-address:9090/api/report',
    headers: {
        "Content-Type": "multipart/form-data",
        "accept": "*/*"
    },
    formData: {
        file: fs.createReadStream('allure-results.zip'),
        filetype: 'application/x-zip-compressed',
        filename: 'samplefilename',
        channels: 'sample',
        title: 'sampletitle',
    },
}, function(error, response, body) {
    console.log(body);
});

But I am getting the response undefined and I am not sure how to pass allureResults=@allure-results.zip. Please help.

Hey, did you solve it?

@rakeshnambiar
Copy link

@spyinfo I didn't manage to resolve it. Let me know in case you have any workaround.

@josefanostylus
Copy link

how to do this using the native Node Js APIs (without the request dependency) ?

@vaibhavgond4557
Copy link

Hey I am confused about one thing, the boundary always be changes with each request, so how is it handled here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment