How to upload images via API with Node.js?

This has been asked various times, but none of the solutions so far work for me.

I’m using Node.js to upload images for a post via the API.

I can create posts no problem, but images will not upload.

I convert the image (a png file) into a binary string.

Then I post an application/json request with the API creds in the headers, and a payload like this:

{
   type: "composer",
   synchronous: true,
   file: `${binary_string}`
}

I’ve tried both file as the field name for the file as the API docs say, and also files[] as another post in the Forum suggested.

Using file, I get a 422 with:

undefined method `tempfile' for #<String:0x00007fdfeba2b1d8>

Using files[], I get a 422 error, with “Unprocessable Entity”.

As well as application/json as the docs say, I’ve also tried it as multipart/form-data as the Web UI does and this poster did.

For the form-data post, I did it like this:

 const form = new FormData();
 form.append("type", "composer");
 form.append("synchronous", "true");
 form.append("files[]", this.stringToBinary(file.data!), {
      contentType: file.mimetype,
 });

http.post("/uploads.json", form.getBuffer(), {
  headers: form.getHeaders(),
 }) // creds get added

With this one, with either file or files[], I get a 422 with the error:

undefined method `tempfile' for #<String:0x00007fdfeba2b1d8>

Can anyone help?

I got this to work, like this:

import Axios from "axios";
import FormData from "form-data";
import fs from "fs";
const http = Axios.create({
  baseURL: "https://forum.zeebe.io",
  headers: {
    "Api-Key":
      "...",
    "Api-Username": "...",
    "Content-Type": "application/json",
    Accept: "application/json",
  },
});
http.interceptors.request.use((config) => {
  if (config.data instanceof FormData) {
    Object.assign(config.headers, config.data.getHeaders());
  }
  return config;
});

const filename = "/Users/sitapati/Desktop/process.png";
const file = fs.readFileSync(filename);
const form = new FormData();
form.append("files[]", file, {
  filename,
});

http
  .post("/uploads.json", form, {
    params: {
      type: "composer",
      synchronous: true,
    },
  })
  .then(({ data }) => {
    console.info("Response from Discourse", JSON.stringify(data, null, 2));
    return {
      url: data.url,
    };
  })
  .catch((e) => {
    console.error(
      "Error uploading file to Discourse",
      JSON.stringify(e, null, 2)
    );
    throw e;
  });

It also works with a file retrieved from a remote URL, which needs to be retrieved like this:

Axios.get(file.slackUrl, {
   responseType: "arraybuffer",
   headers: { Authorization: "Bearer " + this.slackToken },
})

The key here is: responseType: "arraybuffer".

7 Likes