Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I send a binary data (blob) using fetch and FormData?

The following code works as expected. Open the page "https://wiki.epfl.ch/" on Google Chrome, and execute this code on the Developer console. Note: the page "https://wiki.epfl.ch/test.php" does not exists and so it fails to load, but that's not the issue.

response = await fetch("https://wiki.epfl.ch/lapa-studio/documents/DTS/laser%20tutorial.pdf");
response.text().then(function(content) { 
  formData = new FormData();
  console.log(content.length);
  console.log(content);
  formData.append("content", content);

  fetch("https://wiki.epfl.ch/test.php", {method: 'POST', body: formData});
})

It logs:

content.length: 57234
content: %PDF-1.3
%���������
4 0 obj
<< /Length 5 0 R /Filter /FlateDecode >>
stream
x��K��F�����;¢�
...

Go to the Developer Network tab, choose the 'test.php' page, navigate to "Requested payload:" and you can see this content:

------WebKitFormBoundaryOJOOGb7N43BxCRlv
Content-Disposition: form-data; name="content"

%PDF-1.3
%���������
4 0 obj
<< /Length 5 0 R /Filter /FlateDecode >>
stream
...
------WebKitFormBoundaryOJOOGb7N43BxCRlv

The issue is that the request file is a binary file (PDF), and the text gets "mangled". It reports a size of 57234 bytes, when the actual file size (as fetched with a wget command) is 60248 bytes.

The question is: How to get and send the binary data, without being modified?


I tried replacing response.text() by response.blob(), as follows:

response = await fetch("https://wiki.epfl.ch/lapa-studio/documents/DTS/laser%20tutorial.pdf");
response.blob().then(function(content) { 
  console.log(content.size);
  console.log(content);
  formData = new FormData();
  formData.append("content", content);

  fetch("https://wiki.epfl.ch/test.php", {method: 'POST', body: formData});
})

Now I get this log, with the correct file size:

content.size:  60248
content:  Blob(60248) {size: 60248, type: "application/pdf"}

However, going to the Developer Network tab, choose the 'test.php' page, navigate to "Requested payload:", it shows that it sends an empty payload:

------WebKitFormBoundaryYoibuD14Ah2cNGAd
Content-Disposition: form-data; name="content"; filename="blob"
Content-Type: application/pdf


------WebKitFormBoundaryYoibuD14Ah2cNGAd--

Note: The webpage I am developing is not at wiki.epfl.ch. I provide this example so that users can try it (and avoid the "Cross-Origin Resource Sharing" problem). My "test.php" page is in php and $_POST['content'] returns the content when using response.text(), but it returns empty when using response.blob(). So, even if it is the case that the Developer Network tab "Requested payload:" does not show binary data, this snipped is still not working.


The question is: How to get and send the binary data, without being modified?

like image 709
David Portabella Avatar asked Jan 25 '18 16:01

David Portabella


People also ask

How do you send binary data?

Sending binary dataThe send method of the XMLHttpRequest has been extended to enable easy transmission of binary data by accepting an ArrayBuffer , Blob , or File object. The following example creates a text file on-the-fly and uses the POST method to send the "file" to the server.

How do you get binary from a BLOB?

Convert the blob to an ArrayBuffer (see 2 methods). Create an ArrayBufferView (Int8array in this case), spread it into an array, and then map the view to the binary representation of each number using Number. toString() with a radix of 2 - . toString(2) .


2 Answers

If you want to send a binary file, do not use the .text() method, as that returns the file decoded using UTF-8, which is not what you want. Instead, use the .blob() method, which does not attempt to decode the file, and use it directly as the body parameter of the second fetch() since it's allowed to be a Blob:

const response = await fetch("https://wiki.epfl.ch/lapa-studio/documents/DTS/laser%20tutorial.pdf");
const content = await response.blob();
console.log(content.size);
fetch("https://wiki.epfl.ch/test.php", { method: 'POST', body: content });

For parsing this upload format see this answer.

If you want to upload it as part of a multipart/form-data formatted attachment, you can still use the FormData API, but doing so isn't necessary to send the binary data to your PHP script. Just for completeness, here's how you'd do that instead:

const response = await fetch("https://wiki.epfl.ch/lapa-studio/documents/DTS/laser%20tutorial.pdf");
const content = await response.blob();
console.log(content.size);
const formData = new FormData();
formData.append("content", content);
fetch("https://wiki.epfl.ch/test.php", { method: 'POST', body: formData });
like image 94
Patrick Roberts Avatar answered Sep 22 '22 16:09

Patrick Roberts


Try this, by converting blob to DataURL string, you can send binary data without being modified.

response = await fetch("https://wiki.epfl.ch/lapa-studio/documents/DTS/laser%20tutorial.pdf");
response.blob().then(function (content) {

    let reader = new FileReader();

    reader.addEventListener("loadend", function () {

      formData = new FormData();
      formData.append("content", reader.result);
      fetch("https://wiki.epfl.ch/test.php", { method: 'POST', body: formData });
      reader.removeEventListener("loadend");

    });

    reader.readAsDataURL(content);

 });
like image 28
Waseem Ahmed Avatar answered Sep 22 '22 16:09

Waseem Ahmed