I was working on testing upload images to Cloudflare using their API. The task was supposed to be simple because cloudfare have some code samples here, however I was having trouble getting it to work. As I’m using node+axios , the sample code on document is like

var axios = require("axios").default;

var options = {
  method: 'POST',
  url: 'https://api.cloudflare.com/client/v4/accounts/account_identifier/images/v1',
  headers: {
    'Content-Type': 'multipart/form-data; boundary=---011000010111000001101001',
    'X-Auth-Email': ''
  },
  data: '...'
};
axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

The problem is it doesn’t say how to build this data string, initially I (OK I confess it was from gpt) build the request method like

const imageBinary = fs.readFileSync(imagePath);
formData.append('file', imageBinary);
const headers = {
    Authorization: `Bearer ${apiToken}`,
    'Content-Type': `multipart/form-data; boundary=${formData_boundary}`,
};
const response = await axios.post(
    `https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v1`,
    formData,
    { headers }
);

It returned a 415 error, and the detail is “‘ERROR 5455: Uploaded image must have image/jpeg, image/png, image/webp, image/gif or image/svg+xml content-type\n”

I natually thought it was a file format problem, however I checked it was a png file, I even used a jpeg file for test, still got the same.

I then tried to use readFileStream and readFile in promise way, still got the same error.

Then I checked https://developer.mozilla.org/en-US/docs/Web/API/FormData/append and found that I can give it a third parameter to specify the filename although it’s optional, but worth a try, so I changed the code to

formData.append('file', imageBinary, 'test.png');

Then I got error " TypeError: Failed to execute ‘append’ on ‘FormData’: parameter 2 is not of type ‘Blob’"

So what is Blob? I checked https://developer.mozilla.org/en-US/docs/Web/API/Blob and found that it is a file-like object, anyway coplilot had already given me a hint, so I changed the code to

const blobImage = new Blob([imageBinary.buffer]);
formData.append('file', blobImage, 'test.png');

and it finally worked!

So the final code is like

const imageBinary = fs.readFileSync(imagePath);
    const formData = new FormData();
    const blobData = new Blob([imageBinary.buffer]);
    formData.append("file", blobData, "test.png");
    const headers = {
      Authorization: `Bearer ${apiToken}`,
      "Content-Type": `multipart/form-data; boundary=${formData._boundary}`,
    };

    const response = await axios.post(
      `https://api.cloudflare.com/client/v4/accounts/${accountId}/images/v1`,
      formData,
      { headers }
    );

My speculation based on my limited node experince is that if you pass two parameters to formdata.append, the second parameter would be considered a String. While on cloudflare’s end, it requires you to specify the file type , so you have to pass some parameter to tell it.

I wish cloudflare could have given a more detailed example, but anyway I hope this can help someone who is having the same problem.