Como fazer upload de imagens via API com Node.js?

Isso já foi perguntado várias vezes, mas nenhuma das soluções até agora funcionou para mim.

Estou usando Node.js para fazer upload de imagens para um post via API.

Consigo criar posts sem problemas, mas as imagens não são carregadas.

Converso a imagem (um arquivo PNG) em uma string binária.

Em seguida, envio uma solicitação application/json com as credenciais da API nos cabeçalhos e um payload como este:

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

Testei tanto file como nome do campo para o arquivo, conforme indicado na documentação da API, quanto files[], como sugerido em outro post no Fórum.

Ao usar file, recebo um erro 422 com:

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

Ao usar files[], recebo um erro 422 com “Unprocessable Entity”.

Além de application/json, conforme indicado na documentação, também tentei multipart/form-data, como faz a interface web e como fez este usuário.

Para o post com form-data, fiz assim:

 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(),
 }) // as credenciais são adicionadas

Com essa abordagem, tanto com file quanto com files[], recebo um erro 422 com a mensagem:

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

Alguém pode ajudar?

Consegui fazer isso funcionar, assim:

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("Resposta do Discourse", JSON.stringify(data, null, 2));
    return {
      url: data.url,
    };
  })
  .catch((e) => {
    console.error(
      "Erro ao enviar arquivo para o Discourse",
      JSON.stringify(e, null, 2)
    );
    throw e;
  });

Também funciona com um arquivo recuperado de uma URL remota, que precisa ser obtido assim:

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

A chave aqui é: responseType: "arraybuffer".

7 curtidas