Skip to content

Instantly share code, notes, and snippets.

@tanaikech
Last active September 13, 2024 04:49
Show Gist options
  • 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) ?

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