5

At this point in my code, I've got an img variable which contains a base-64-encoded image. I've confirmed that the base-64 image is correct by logging img and pasting the output into the code beautify base-64 image encoder.

Now, I'm trying to figure out how to POST to the Slack API. I've read the files.upload method API documentation for Slack, which says they accept multipart form data or application/x-www-form-urlencoded, but I don't know how to convert from Base-64 to either of those from node.js. Once I've done the conversion, I also don't know how to make the actual post using request.post, as it's unclear to me how to encode the relevant information for the slack request.

Currently, I'm trying something like this:

        request.post({ url: 'https://slack.com/api/files.upload',
          formData: {
            token: slackToken,
            tile: "Image",
            filename: "image.png",
            filetype: "auto",
            channels: "testing",
            file: img,
          },
        }, function (err, response) {
            // just for debugging
            console.log(JSON.parse(response.body));
        });

Getting response: { ok: false, error: 'no_file_data' }, which makes sense, as Slack does not accept base-64 encoding of img

Any help much appreciated!

a94
  • 564
  • 1
  • 7
  • 16
  • you need to convert your base64 encoded image into a regular image file before uploading, e.g. PNG. this answer apparently explains how to do this with nodejs: https://stackoverflow.com/questions/6926016/nodejs-saving-a-base64-encoded-image-to-disk – Erik Kalkoken Apr 28 '19 at 12:54
  • Thanks Eric! As far as I can tell, those answers seem to be focused on writing the file directly to the file system. When I tried doing the same but storing it as a variable, it came back undefined: `let imgAsBase64 = img.substring(img.indexOf(',') + 1) let imgAsFile = await require('fs').writeFile('image.png', imgAsBase64, 'base64', (err) => { console.log(err); })` => `imgAsFile'`posts as undefined, so I get a 'cannot read property of undefined' – a94 Apr 28 '19 at 14:30
  • Apparently you can convert a base64 image to an object with `Buffer`. See this article for details: https://stackabuse.com/encoding-and-decoding-base64-strings-in-node-js/ – Erik Kalkoken Apr 28 '19 at 15:49
  • amazing! wasn't able to figure it out using the Buffer, but instead, based on the bottom of the article, I used `fs.writeFileSync` to create a file directly from `base64` (truncated to remove the metadata at the beginning) and then used that file path as the `file` argument in the post. Would you like to post this as an answer, so this question doesn't remain in the `unanswered` category? – a94 Apr 28 '19 at 17:02
  • Glad it works now. Since you found the solution yourself I think it would be best if you also post it. Incl. how the final code looks. – Erik Kalkoken Apr 28 '19 at 18:30

1 Answers1

6

First, we'll need to trim the base64 encoding if it starts with: data:image/png;base64,

Now the file system can help us with converting to a file with writeFileSync, which accepts a base64 encoding of a file and writes a file to the system. I'd like to find a way to do this without the side effect of writing a file to the system, but wasn't able to yet.

Once that's ready, we can make our slack post request with the path to the newly created file as the file parameter. Working code below:

let imgAsBase64 = img.substring(img.indexOf(',') + 1);

require('fs').writeFileSync('image.png', imgAsBase64, 'base64', (err) => {
  console.log(err);
});

await request.post(
  {
    url: 'https://slack.com/api/files.upload',
    formData: {
      token: slackToken,
      tile: 'Image',
      filename: 'image.png',
      filetype: 'auto',
      channels: msg.channel,
      file: require('fs').createReadStream('./image.png')
    }
  },
  function (err, response) {
    // just for debugging
    console.log(response.body);
  }
);
Inshal Irshad
  • 227
  • 3
  • 17
a94
  • 564
  • 1
  • 7
  • 16
  • File could be either Stream or Buffer. So we can create buffer from base64. `const buffer = Buffer.from(imgAsBase64 , 'base64');` And then then pass it like this `... file: buffer ...` – salkcid May 02 '22 at 13:21