19

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.

starball
  • 20,030
  • 7
  • 43
  • 238
David Portabella
  • 12,390
  • 27
  • 101
  • 182
  • _"Maybe the problem is that `response.blob()` returns a Blob object"_ How is `Body.blob()` related to the Question? – guest271314 Jan 25 '18 at 16:53
  • i've updated the question with more details – David Portabella Jan 28 '18 at 19:44
  • 1
    The second approach (with `blob()`) does actually send data. I presume that the Chrome dev tools don't show the value because it would be garbled binary data, but if you watch the request in Fiddler, you can see that data is sending. Do you have some way to check whether it's being received correctly? As far as I can tell, https://wiki.epfl.ch/test doesn't actually show any confirmation that a file was received (is it even intended for that purpose?) – JLRishe Jan 29 '18 at 07:28
  • The webpage I am developing is not at wiki.epfl.ch. I provide this example so that users can try (and avoid the "Cross-Origin Resource Sharing" problem). My "test" page is in php and `$_POST['content']` returns empty. – David Portabella Jan 29 '18 at 08:46
  • @DavidPortabella I just tried out what you showed. It is in accordance with [Sending Binary Data](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data), but from what I see, the request is probably not being shown in the payload, but its probably getting attached. I think so for 2 reasons. (a) `formData.get("content")` returns the required blob and (b) The content length of the request header is `60434` indicating that the pdf is probably there. – TheChetan Jan 29 '18 at 20:08
  • 1
    See: https://bugs.chromium.org/p/chromium/issues/detail?id=457484, very relevant to what you're facing. – TheChetan Jan 29 '18 at 20:18
  • 1
    i don't think binary form chunks are shown in chrome, ever. – dandavis Jan 30 '18 at 07:09

3 Answers3

20

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 });
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
9

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

 });
Waseem Ahmed
  • 164
  • 3
  • 6
    FYI using the `FileReader` API here to buffer the `Blob` is completely unnecessary. [`formData.append()`](https://developer.mozilla.org/en-US/docs/Web/API/FormData/append) accepts `content` directly as a `value` rather than buffering it to `reader.result`. – Patrick Roberts Feb 05 '18 at 13:47
  • To see how to use `FormData` without the `FileReader`, see the second part of [my answer](https://stackoverflow.com/a/48568899/1541563). – Patrick Roberts Feb 19 '20 at 19:37
0

Please Try with this code may hope it will helps you

 pdfcall(){
var xmlhttp = new XMLHttpRequest(),
method = 'GET',
 xmlhttp.open(method, url, true);
  xmlhttp.responseType = 'blob';
  xmlhttp.onload = (e: any) => {
   console.log(xmlhttp);
if (xmlhttp.status === 200) {
  let blob = new Blob([xmlhttp.response], {type: 'application/pdf'});
  this.pdfSrc = URL.createObjectURL(blob);
  }
};
 xmlhttp.onerror = (e: any) =>{
  console.error(e,'eerr')
    }
  xmlhttp.send();
}
Manoj Bhardwaj
  • 736
  • 1
  • 9
  • 25