jimmywarting / FormData

HTML5 `FormData` polyfill for Browsers and nodejs
MIT License
360 stars 102 forks source link

Request fails while using `fs.createReadStream` #138

Closed HarshKapadia2 closed 2 years ago

HarshKapadia2 commented 2 years ago

The code below works with the form-data package, but not with this package. I cannot figure out why. Can anyone please help?

Code

// import FormData from "form-data"; // Works with this
import { FormData } from "formdata-polyfill/esm.min.js"; // Does not work with this
import fetch from "node-fetch"; // v3
import { createReadStream } from "fs";

async function predictUsingFile(filePath) {
      const fileStream = createReadStream(filePath);

      const formData = new FormData();
      formData.append("file", fileStream);
      formData.append("modelId", this.modelId);

      const response = await fetch(
            `https://app.nanonets.com/api/v2/ImageCategorization/LabelFile`,
            {
                  method: "POST",
                  headers: {
                        "Authorization": this.authHeaderVal,
                        "Accept": "application/json"
                  },
                  body: formData
            }
      );
      const data = response.json();

      return data;
}

Error response

{
  message: 'Bad Request',
  code: 400,
  errors: [
    {
      reason: 'File or url missing from request',
      message: 'Add file or url in request as explained here: https://app.nanonets.com/documentation#authentication'
    }
  ]
}
jimmywarting commented 2 years ago

FormData length needs to be calculated and the formdata specification can't do that using a readable stream. this is what have made form-data so complicated to use. it's only suppose to accept strings, blobs & files. (this blobs have a size property that can be read without reading everything into memory before posting it)

what you can do instead is to import fetch-blob that we use internally and do something like this:

// import FormData from "form-data"; // Works with this
import { FormData } from "formdata-polyfill/esm.min.js"; // Does not work with this
import { fileFromSync } from "fetch-blob/from.js";
import fetch from "node-fetch"; // v3
// import { createReadStream } from "fs";

async function predictUsingFile(filePath) {
      // const fileStream = fileFromSync(filePath);
      const file = fileFromSync(filePath); // this will only stat the file for getting the file size

      const formData = new FormData();
      formData.append("file", file);
      formData.append("modelId", this.modelId);

      const response = await fetch(
            `https://app.nanonets.com/api/v2/ImageCategorization/LabelFile`,
            {
                  method: "POST",
                  headers: {
                        "Authorization": this.authHeaderVal,
                        "Accept": "application/json"
                  },
                  body: formData
            }
      );

      return response.json();
}
jimmywarting commented 2 years ago

fyi: NodeJS already have a Blob class, but it's pretty useless as of now.... unless we are able to get a file backed up by the filesystem then it has no useful benefits. They want to change this so they can implement a FormData among other things. Give this issues a 👍 if you want to see it prioritized :)

jimmywarting commented 2 years ago

there is also a 2nd way to append files and avoid using fetch-blob, they just need to look like a file or blob

fd.append('name', {
  [Symbol.toStringTag]: 'File'
  size: 123,
  name: 'foo.txt',
  stream() {
    return readableStream
  }
})