oven-sh / bun

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one
https://bun.sh
Other
74.15k stars 2.77k forks source link

`fetch` upload with formdata is different than node #7917

Open joulev opened 10 months ago

joulev commented 10 months ago

What version of Bun is running?

1.0.20+09d51486e

What platform is your computer?

Darwin 23.2.0 arm64 arm

What steps can reproduce the bug?

Run this JavaScript file

// test.mjs
const form = new FormData();
form.append("filename[]", "document.tex");
form.append(
  "filecontents[]",
  "\\documentclass{article}\\begin{document}Hello world\\end{document}"
);
form.append("return", "pdf");
const pdfResponse = await fetch("https://texlive.net/cgi-bin/latexcgi", {
  method: "POST",
  body: form,
});
const buffer = await pdfResponse.arrayBuffer();
console.log(buffer);

What is the expected behavior?

If you run the file above with node, it gets the correct data (which is a PDF file):

ArrayBuffer {
  [Uint8Contents]: <25 50 44 46 2d 31 2e 35 0a 25 d0 d4 c5 d8 0a 33 20 30 20 6f 62 6a 0a 3c 3c 0a 2f 4c 65 6e 67 74 68 20 39 34 20 20 20 20 20 20 20 20 0a 2f 46 69 6c 74 65 72 20 2f 46 6c 61 74 65 44 65 63 6f 64 65 0a 3e 3e 0a 73 74 72 65 61 6d 0a 78 da 25 ca 3b 0a 80 30 0c 00 d0 bd a7 c8 d8 0e 8d 26 fd a4 ae 82 22 ce ... 12260 more bytes>,
  byteLength: 12360
}

What do you see instead?

But when you run it with bun, it gets this textual response

ArrayBuffer(41) [ 66, 97, 100, 32, 102, 111, 114, 109, 32, 116, 121, 112, 101, 10, 32, 66, 97, 100, 32, 70, 111, 114, 109, 58, 32, 110, 111, 32, 109, 97, 105, 110, 32, 100, 111, 99, 117, 109, 101, 110, 116 ]

Bad form type Bad Form: no main document

Additional information

This particular URL response is a 301 Moved Permanently, then when you follow the redirect you get the URL of the PDF file that you download to the big ArrayBuffer above. Maybe it is at this point that Bun's fetch acts differently from Node's?

joulev commented 10 months ago

For more information, the following code snippet (thanks to @/RiskyMH), with a rather manually dirty way of submitting multipart/form-data, works

const form = ...;
const textBody = await new Response(form).text();
const pdfResponse = await fetch("https://texlive.net/cgi-bin/latexcgi", {
  method: "POST",
  body: textBody,
  headers: {
    "Content-Type": `multipart/form-data; boundary=${textBody
      .split("\n")[0]
      .slice(2)}`,
  },
});
const buffer = await pdfResponse.arrayBuffer();
console.log(buffer);
Yefori-Go commented 6 months ago

For more information, the following code snippet (thanks to @/RiskyMH), with a rather manually dirty way of submitting multipart/form-data, works

const form = ...;
const textBody = await new Response(form).text();
const pdfResponse = await fetch("https://texlive.net/cgi-bin/latexcgi", {
  method: "POST",
  body: textBody,
  headers: {
    "Content-Type": `multipart/form-data; boundary=${textBody
      .split("\n")[0]
      .slice(2)}`,
  },
});
const buffer = await pdfResponse.arrayBuffer();
console.log(buffer);

This method seems to only be applicable for text transmission, not for transmitting images or other binary data. I encountered this problem as well (boundary="xxxx", with extra double quotes causing the destination server to fail in parsing it correctly).

Dilettante258 commented 3 months ago

Same error here😿

gregsuper commented 1 month ago

For more information, the following code snippet (thanks to @/RiskyMH), with a rather manually dirty way of submitting multipart/form-data, works

const form = ...;
const textBody = await new Response(form).text();
const pdfResponse = await fetch("https://texlive.net/cgi-bin/latexcgi", {
  method: "POST",
  body: textBody,
  headers: {
    "Content-Type": `multipart/form-data; boundary=${textBody
      .split("\n")[0]
      .slice(2)}`,
  },
});
const buffer = await pdfResponse.arrayBuffer();
console.log(buffer);

This method seems to only be applicable for text transmission, not for transmitting images or other binary data. I encountered this problem as well (boundary="xxxx", with extra double quotes causing the destination server to fail in parsing it correctly).

My solution for sending (uploading) pdf was this:

const formData = new FormData();
formData.append('fileupload', Bun.file(`files/${name}`), name);
const blob = await new Response(formData).blob();
const response = await fetch("https://someurl.com/upload/pdf", {
  method: "POST",
  body: await blob.arrayBuffer(),
  headers: {
    "Content-Type": blob.type
  },
});
mdagit commented 3 weeks ago

As far as I can tell, Bun doesn't come close to handling a post body of type FormData correctly. Kind of a big miss, and I'm disappointed it is approaching a year now and nothing has been done.