Cómo subir imágenes a través de la API con Node.js?

Esto se ha preguntado varias veces, pero ninguna de las soluciones hasta ahora me ha funcionado.

Estoy usando Node.js para subir imágenes a una publicación mediante la API.

Puedo crear publicaciones sin problemas, pero las imágenes no se suben.

Convierto la imagen (un archivo PNG) en una cadena binaria.

Luego, envío una solicitud application/json con las credenciales de la API en los encabezados y un payload como este:

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

He probado tanto file como nombre del campo para el archivo, tal como indican los documentos de la API, como también files[], como sugirió otro post en el Foro.

Al usar file, obtengo un error 422 con el siguiente mensaje:

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

Al usar files[], obtengo un error 422 con “Unprocessable Entity”.

Además de application/json, tal como indican los documentos, también he probado con multipart/form-data, como lo hace la interfaz web y como hizo este usuario.

Para la publicación con form-data, lo hice de la siguiente manera:

 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(),
 }) // las credenciales se agregan aquí

Con esto, ya sea con file o files[], obtengo un error 422 con el mensaje:

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

¿Puede alguien ayudarme?

Logré que esto funcione así:

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("Respuesta de Discourse", JSON.stringify(data, null, 2));
    return {
      url: data.url,
    };
  })
  .catch((e) => {
    console.error(
      "Error al subir el archivo a Discourse",
      JSON.stringify(e, null, 2)
    );
    throw e;
  });

También funciona con un archivo obtenido desde una URL remota, que debe recuperarse de la siguiente manera:

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

La clave aquí es: responseType: "arraybuffer".

7 Me gusta