2

I have a PHP file that returns output in PDF - Works fine if I access the file directly.

I'd like to retrieve the PDF file through AJAX.

In native Javascript, it works fine:

  var req = new XMLHttpRequest();
  req.open("POST", "./api/pdftest.php?wpid="+wpid, true);
  req.responseType = "blob";
  req.onreadystatechange = function ()
   {
    if (req.readyState === 4 && req.status === 200)
     {
      var blob=req.response;
      var filename = "test.pdf";
      var link = document.createElement('a');
      link.href = window.URL.createObjectURL(blob);
      link.download = "test.pdf";
      link.click();
      var file = new File([blob], filename, { type: 'application/force-download' });
      window.open(URL.createObjectURL(file));
     }
   };
  req.send();

But I guess I'd use jQuery to ensure cross browser compatibility (although the snippet above works in Edge, Chrome and Firefox on pc, I haven't tested it in other browsers/on other platforms)

So I tried to rewrite the function:

  url='./api/pdftest.php?wpid='+wpid;
  $.ajax(
   {
    url: url,
    method: 'POST',
    responseType: 'blob',
    success: function(data)
     {
      var filename='test.pdf';
      var blob=new Blob([data]);
      var filename = "test.pdf";
      var link = document.createElement('a');
      link.href = window.URL.createObjectURL(blob);
      link.download = "test.pdf";
      link.click();
      var file = new File([blob], filename, { type: 'application/force-download' });
      window.open(URL.createObjectURL(file));
     }
   });

The jQuery equivalent allows me to download a PDF file but … the PDF file is empty.

So I guess I am doing something wrong, probably in the DATA to BLOB conversion. But what? I hope somebody can see what I am doing wrong.

I've been using ages on StackOverflow, read many suggestions - but didn't find any answer. I simply can't see the forest for the trees.

miken32
  • 42,008
  • 16
  • 111
  • 154
Kairos
  • 147
  • 1
  • 12

3 Answers3

3

Looking at the documentation for the jQuery.ajax() function, we see there's no setting called responseType, so you need to use xhrFields to directly set a property of the XHR object. And, since you're only setting the URL and success callback, we can just use the shorter jquery.post() function.

So the data is returned, we make a Blob and then a URL to download it. I'm not on Windows so I can't test if that link I constructed will work as expected, but figured I'd do it the jQuery way.

var url = './api/pdftest.php?wpid=' + wpid;
$.post({
    url: url,
    xhrFields: {responseType: "blob"},
    success: function(data) {
        // don't set the MIME type to pdf or it will display
        var blob = new Blob([data], {type: "application/octet-stream"});
        // build a blob URL
        var bloburl = window.URL.createObjectURL(blob);
        // trigger download for edge
        var link = $("<a>").attr({href: bloburl, download: "test.pdf"}).click();
        // trigger download for other browsers
        window.open(bloburl);
    }
});
miken32
  • 42,008
  • 16
  • 111
  • 154
  • Tried your solution in Chrome and Firefox, and that allows me to download a PDF - but it is still an empty PDF. In Edge, it won't download at all. The reason I create a link and put a ".click()" on it, AND try to force download the file is because Firefox does not accept the "click" method, while Edge does not accept the force download method (at least not the one I used) – Kairos Nov 23 '18 at 07:52
  • Ok I tried your live version. First off, saying a downloaded file is "empty" means something very specific when you're talking to a programming crowd. It means empty, as in zero bytes. Your problem is that the PDF is a *blank page*, and that's absolutely down to the response of the server. You'll need to debug why your server is returning a PDF without text under certain circumstances. – miken32 Nov 23 '18 at 17:37
  • A simple check with the web inspector reveals that your native Javascript version, for some reason, is not sending two headers: `X-Requested-With: XmlHttpRequest` and `TE: Trailers`. Otherwise the two requests are identical. – miken32 Nov 23 '18 at 17:37
  • Correct, I used a wrong term: the PDF is blank, not empty. But: if I access the php file directy (https://kairosplanner.com/api/pdftest.php) or use the native XMLHTTPRequest, the PDF is not blank; it shows the current time in the upper left corner. The PDF/PHP file is not the problem. I guess you were on the right track when you said that $.Ajax does not support responseType. – Kairos Nov 23 '18 at 20:36
  • No, your server is returning a blank PDF; that's where the problem is. – miken32 Nov 23 '18 at 20:38
  • Ok, if you say so. – Kairos Nov 23 '18 at 22:20
  • Anyone else, who gets the current time in the upper left corner when opening https://kairosplanner.com/api/pdftest.pdf, have a comment? – Kairos Nov 23 '18 at 22:21
  • Well, it turns out, that binary data and jQuery.ajax does not work together. That's why it simply does not work. There is nothing wrong with my PHP file, I simply need to use a native XMLHTTPRequest - as miken32 pointed out before: jquery.Ajax does not support "responseType" - and that is the whole problem. So: the way to go is: use native XMLHTTPrequest, and find a cross-browser method to download the file. – Kairos Nov 24 '18 at 08:51
  • Sorry I doubted you ;) It just seemed really unlikely that the binary data would be corrupted in just the right way to erase a small text field but leave the rest of the document intact. Anyway, I did some reading and found the `xhrFields` setting, which should do the trick for you. – miken32 Nov 25 '18 at 04:46
  • Never mind, Miken32. I didn't want to discuss whether I was right **or not**, other people's opinion would not change it, would it? ;) You send me in the right direction (and that's what counts), and now you've done it again - thanks for that! I will try to find out about xhrFields. I already read something about that, but didn't see it as a solution. I also found this: https://blog.jayway.com/2017/07/13/open-pdf-downloaded-api-javascript/ but am not sure how much it helps. The Native Way would not solve my problem, so perhaps xhrfields does. Thanks for your insights. – Kairos Nov 25 '18 at 09:14
1

As binary data is not possible to retrieve through jQuery.ajax, Native is the only way, at least for now. The following method works in Edge, Firefox, Chrome and Opera - tested on WIndows 10.

    var req = new XMLHttpRequest();
    req.open("POST", "./api/pdftest.php?wpid="+wpid, true);
    req.responseType = "blob";
    req.onreadystatechange = function ()
     {
      if (req.readyState === 4 && req.status === 200)
       {
        var blob=req.response;
        var filename = "test.pdf";
        var link = document.createElement('a');
        link.setAttribute("type", "hidden"); // make it hidden if needed
        link.href = window.URL.createObjectURL(blob);
        link.download = "test.pdf";
        document.body.appendChild(link);
        link.click();
        link.remove();
        var file = new File([blob], filename, { type: 'application/force-download' });
        //window.open(URL.createObjectURL(file));
       }
     };
    req.send();
Kairos
  • 147
  • 1
  • 12
1

Probably double!

This is the solution I found thanks to Hisham at Download pdf file using jquery ajax:

First, add the following plugin that can be used to the XHR V2 capabilities missing in JQuery: https://github.com/acigna/jquery-ajax-native

Then:

   url='./api/pdftest.php?wpid='+wpid;
   $.ajax(
    {
     dataType: 'native',
     url: url,
     xhrFields:
      {
       responseType: 'blob'
      },
     success: function(blob)
      {
       var filename = "test.pdf";
       var link = document.createElement('a');
       link.href = window.URL.createObjectURL(blob);
       link.download = "test.pdf";
       link.click();
       var file = new File([blob], filename, { type: 'application/force-download' });
       window.open(URL.createObjectURL(file));
      }
    });

This seems to be working. Note: the window.open() is to make download possible in Firefox, the link.click() method Works in Edge, Chrome and Opera

Thanks to miken32 for pointing into the right direction.

Kairos
  • 147
  • 1
  • 12