Search code examples
node.jsfile-uploadupload

Error when trying to upload an image to ImgBB


I'm trying to upload images to ImgBB using NodeJS and GraphQL. I have a uploadImage mutation that takes an image in the form of a data url string, and goes as follows:

import parseDataUrl from "data-uri-to-buffer";

// ...

{
  Mutation: {
    async uploadImage(_, { dataUrl }) {
      const buffer = parseDataUrl(dataUrl); // https://www.npmjs.com/package/data-uri-to-buffer
      
      if (buffer.byteLength > 10 * 10 ** 6)
        throw new Error("The image exceeds the maximum size of 10 MB");

      const body = new FormData();
      body.append("image", buffer);

      const result = await fetch(
        `https://api.imgbb.com/1/upload?key=${process.env.IMGBB_KEY}`,
        {
          method: "post",
          headers: { ...body.getHeaders() },
          body
        }
      ).then<any>(result => result.json());

      if (!result.success || !result.url) {
        const msg = result.error?.message;
        throw new Error(
          `There was an error during upload${msg ? `: ${msg}` : ""}`
        );
      }
      return result.url;
    }
  }
}

body.getHeaders() contains:

{
    'content-type': 'multipart/form-data; boundary=--------------
------------656587403243047934588601'
  }

(I'm using node-fetch)

But no matter the combinations of query params, headers and body I use, I always end up getting this error:

  {
    status_code: 400,
    error: { message: 'Undefined array key "scheme"', code: 0 },
    status_txt: 'Bad Request'
  }

I can't find anything about it, do you have an idea?


Solution

  • There are multiple things you can do to resolve your issue.

    1. Important thing with FormData, you need to explicitly provide a name for the image buffer if it's not already included, uploading without name API would throw the same error you have mentioned.
    body.append("image", buffer, "addSomeImageName.png");
    
    1. The api does not require any header explicitly so you can remove it.
    {
        method: "post",
        headers: { ...body.getHeaders() }, // This can be removed.
        body
    }
    
    1. The logic you are using to check for the result is faulty and would always throw error even if the result is successful.
    if (!result.success || !result.url) {  // Check for success flag only.
      const msg = result.error?.message;
      throw new Error(
        `There was an error during upload${msg ? `: ${msg}` : ""}`
      );
    }
    

    This is the block I tested and is working fine:

    import parseDataUrl from "data-uri-to-buffer";
    import fetch from 'node-fetch';
    import FormData from "form-data";
    
    
    async function uploadImage({
      dataUrl
    }) {
    
      const buffer = parseDataUrl(dataUrl);
    
      if (buffer.byteLength > 10 * 10 ** 6)
        throw new Error("The image exceeds the maximum size of 10 MB");
    
      const body = new FormData();
      body.append("image", buffer, "someImageName.png");
    
      const result = await fetch(
        `https://api.imgbb.com/1/upload?key=<your-api-key>`, {
          method: "post",
          // headers: { ...body.getHeaders() },
          body
        }
      ).then(result => result.json())
      // .then(data => {
      //   console.log(data); // Working fine here too.
      // });
    
      console.log("-------result--------\n", result); // Result is fine.
    
      // Logic testing.
      console.log(result.success);
      console.log(result.url);
      console.log(!result.success);
      console.log(!result.url);
      console.log(!result.success || !result.url);
    
    
      if (!result.success || !result.url) {
        const msg = result.error ? .message;
        console.log(`There was an error during upload${msg ? `: ${msg}` : ""}`)
        // throw new Error(
        //   `There was an error during upload${msg ? `: ${msg}` : ""}`
        // );
      }
    
      return result.url;
    }
    
    console.log("--------------------- console result ------------------------")
    console.log(uploadImage({
      dataUrl: "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"
    }));