Search code examples
javascriptfetch-api

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.


Solution

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

    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);
    
     });