0

I'm currently calling an external API in order to get a payslip sent to me (Sent in binary over to my node server as I have to use certificate authentication on the node side)

I've managed to figure out how to get my client to download this on chrome by sending it as a binary object, then using createObjectURL to load it, however this isn't working on any IE versions.

I've tried various solutions but can't seem to get the compatability i'm after, I was wondering if anyone would be able to give advice on what my options are for ensuring IE8 compatibility > on the client side when serving the PDF?

Server is currently sending the recieved PDF data like so

res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'attachment; filename=myPdf.pdf');
res.write(pay_pdf_data, 'binary');
res.end(); 

Current method on client which works fine on chrome but doesn't work in IE / Older browsers

function httpGetAsync(theUrl, data, callback) {
    var request = new XMLHttpRequest();
    request.open("POST", theUrl, true); 
    request.responseType = "blob";
    request.onload = function (e) {
        if (this.status === 200) {
            // `blob` response
            console.log(this.response);
            // create `objectURL` of `this.response` : `.pdf` as `Blob`
            var file = window.URL.createObjectURL(this.response);
            var a = document.createElement("a");
            a.href = file;
            a.download = this.response.name || "detailPDF";
            document.body.appendChild(a);
            a.click();
            // remove `a` following `Save As` dialog, 
            // `window` regains `focus`
            window.onfocus = function () {                     
              document.body.removeChild(a)
            }
        };
    };
    request.setRequestHeader("Content-Type", "application/json");
    request.send(data);
}

Thanks!

does_not_compute
  • 476
  • 1
  • 7
  • 20
  • Are you setting a `Content-Type` header on the output stream to client? – thinice Mar 23 '17 at 15:22
  • 2
    Why would client be using ie8? – guest271314 Mar 23 '17 at 15:25
  • @guest271314 You tell me.. All I know is we have to support offices locked down on IE8 unfortunately it's out of my control – does_not_compute Mar 23 '17 at 15:37
  • @thinice I've tried all sorts with the headers, i've set them all correctly i'm sure. What i'm struggling with is how to open these on the client accross all browsers. The download never starts automatically with how it's set up currently. I've included the code from the server – does_not_compute Mar 23 '17 at 15:38
  • Are you trying to embed this into a webpage or as a download? Can you elaborate more as to how you're trying to use this as a client? – thinice Mar 23 '17 at 15:44
  • @thinice would like to present a download link, but nothing i've tried will work the download link never appears unless I use the BLOB method i've now edited above to include. – does_not_compute Mar 23 '17 at 15:47
  • Some things I would try: change your content-disposition to `res.setHeader('Content-Disposition', 'attachment; filename="myPdf.pdf"');`. Depending on the actual name of the file you may want to `urlencode` it. Last, also set the `Content-Length` header – charmeleon Mar 23 '17 at 15:54
  • @charmeleon I've already tried setting all of those with no success on starting the download automatically, Some example clientside code to call would be of great help, Thanks. – does_not_compute Mar 23 '17 at 15:56
  • It's hard to show that without knowing what your delivery method looks like. Do you want to prompt a download? Do you want to open it directly in the browser? Does the user click a link, a button, navigate to a page (GET request) or is redirected to a page (form POST? 30X redirect?)? – charmeleon Mar 23 '17 at 16:13
  • Also, are you sure this is IE8 and not an earlier version? Some earlier versions your `Content-Type` had be `Content-Type: application/download; filename=myPdf.pdf` and also `Content-Disposition: inline; filename=myPdf.pdf` – charmeleon Mar 23 '17 at 16:16
  • @charmeleon I'm running the httpGetAsync function which then makes a POST request to my node server ( due to needing to pass through dates / other stuff related to the PDF I need to retrieve), I then send it back with the headers shown above. I would like to just begin downloading the PDF once it arrives – does_not_compute Mar 23 '17 at 16:16
  • @charmeleon My current code doesn't even work on IE11, so any code which works on 8-11 + chrome would be great. – does_not_compute Mar 23 '17 at 16:17
  • You can try using form approach at http://stackoverflow.com/q/38711803/ – guest271314 Mar 23 '17 at 16:55

1 Answers1

1

IE does not support the download attribute on anchor tags: http://caniuse.com/#search=download.

For IE10/IE11, you can use window.navigator.msSaveOrOpenBlob, but for IE8/IE9 you need to add a hack that involves an invisible iframe on your page:

<iframe name="seekritDownload" style="display:none"></iframe>

As an alternative, you can try this:

function httpGetAsync(theUrl, data, callback) {
  var request = new XMLHttpRequest();
  request.open("POST", theUrl, true); 
  // IE10/IE11 and other modern browsers
  if ('responseType' in request) {
    request.responseType = "blob";
    request.onload = function (e) {
      if (this.status === 200) {
        // `blob` response
        console.log(this.response);
        // IE10/IE11
        if ('msSaveOrOpenBlob' in window.navigator) {
          // Here I assume `this.response` is the blob
          window.navigator.msSaveOrOpenBlob(this.response, this.response.name || "detailPDF");
        }
        else {
          // create `objectURL` of `this.response` : `.pdf` as `Blob`
          var file = window.URL.createObjectURL(this.response);
          var a = document.createElement("a");
          a.href = file;
          a.download = this.response.name || "detailPDF";
          document.body.appendChild(a);
          a.click();
          // remove `a` following `Save As` dialog, 
          // `window` regains `focus`
          window.onfocus = function () {                     
            document.body.removeChild(a)
          }
        }
      }
    };
    request.setRequestHeader("Content-Type", "application/json");
    request.send(data);
  }
  else {
    // This should work for IE8/IE9
    window.open(theUrl, 'seekritDownload');
  }
}

For reference, see: https://msdn.microsoft.com/en-us/library/hh779016(v=vs.85).aspx

EDIT: Besides the missing download attribute, IE8 lacks XMLHttpRequest.responseType and the Blob object. I added a HUGE hack with an iframe that should work. If you prefer, you can create and append the iframe element with JavaScript instead

charmeleon
  • 2,693
  • 1
  • 20
  • 35
  • Ie8 does not support responseType at XMLHttpRequest either. Did you copy this Answer verbatim http://stackoverflow.com/a/31763030/2801559? – guest271314 Mar 23 '17 at 16:45
  • No, I copied the JavaScript verbatim from OP. I did not consider he may have copied it from elsewhere! – charmeleon Mar 23 '17 at 16:49
  • Either way did you try at ie8? – guest271314 Mar 23 '17 at 16:51
  • Did you check if ie8 supports responseType at XMLHttpRequest? – guest271314 Mar 23 '17 at 17:09
  • @charmeleon Thanks for the response, apologies for the late reply I've been away for the weekend. This code works in latest chrome / IE / Firefox but not in anything < IE10, I'm getting "HTTPS security is compromised by res://ieframe.dll/http_404.htm" errors – does_not_compute Mar 28 '17 at 10:40
  • @does_not_compute That error is typically because you're on an HTTPs page and you're trying to load something from an HTTP url. Make sure that your `theUrl` variable uses HTTPS before it's used for the `iframe` – charmeleon Mar 28 '17 at 14:43
  • @charmeleon I'm making a https request and still getting it, I'm also getting 404's from IIS before even displaying it in there, so presumably that's not working for me :( – does_not_compute Mar 28 '17 at 15:00
  • @does_not_compute Is the PDF being downloaded from a different domain or another subdomain? In that case you may try setting the `X-Frame-Options` header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options. Effectively adding `res.setHeader('X-Frame-Options', 'SAMEORIGIN');` to your response headers – charmeleon Mar 28 '17 at 15:25
  • @charmeleon Basically I do a "fake" reverse proxy, Where I call myself and let IIS handle the URL rewrite. For example i'm on "https://example:2000/myPage", From there I click on a button which calls '--https://example:2000/getPDF', IIS then catches this on it's ReWrite rules and calls my node server running on the save server but different port 'http://example:3000/getPDF'. The response if then returned back to the front back through IIS. I can see the headers "X-Frame-Options: SAMEORIGIN" are set however I still get an error. – does_not_compute Mar 28 '17 at 15:29
  • Ah ok, in this case I would think you *don't* want the `X-Frame-Options` header, so make sure you remove it from both the IIS (reverse proxy) and NodeJS (real) servers – charmeleon Mar 28 '17 at 15:43