26

I use this code to download excel file from server.

$.ajax({
    headers: CLIENT.authorize(),
    url: '/server/url',
    type: 'POST',
    contentType: "application/json; charset=utf-8",
    data: JSON.stringify(jsonData),
    success: function (data) {
        alert('Data size: ' + data.length);
        var blob = new Blob([data], { type: "application/vnd.ms-excel" });
        alert('BLOB SIZE: ' + data.length);
        var URL = window.URL || window.webkitURL;
        var downloadUrl = URL.createObjectURL(blob);
        document.location = downloadUrl;
    },
});

The problem I experience is that even though data and blob sizes are identical, the moment document.location gets assigned I'm prompted to download almoste two times larger excel file. And when I try to open it, excel complains about wrong file format and opened file contains a lot of garbage, even though required text is still there.

Any ideas what is causing this and how to avoid it?

SMGhost
  • 3,867
  • 6
  • 38
  • 68
  • I dont think that is a great idea. Why not just let the server push the file to the browser for a normal download stream. This seems very unnatural, unless you have a very specific reason to do this, like some kind of Proxy or on-line analysis, but its just going to thrash browser memeory . – Piotr Kula Apr 01 '15 at 14:36
  • Try setting the content-type to `arrayBuffer` – levi Apr 01 '15 at 14:50
  • @ppumkin, unfortunately I need to meet requirements. I can't store file on server and I need to pass authorization header to get the file. – SMGhost Apr 01 '15 at 15:00
  • @levi, do you mean content type where I specify JSON or when creating blob? – SMGhost Apr 01 '15 at 15:03
  • yes. 10morechars..... – levi Apr 01 '15 at 16:48

1 Answers1

47

So I solved the problem using AJAX 2. It natively supports binary streams. You can't use jQuery for that, unless you base64 encode everything, apparently.

Working code looks like this:

var xhr = new XMLHttpRequest();
xhr.open('POST', '/le/url', true);
xhr.responseType = 'blob';
$.each(SERVER.authorization(), function(k, v) {
    xhr.setRequestHeader(k, v);
});
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');
xhr.onload = function(e) {
    preloader.modal('hide');
    if (this.status == 200) {
        var blob = new Blob([this.response], {type: 'application/vnd.ms-excel'});
        var downloadUrl = URL.createObjectURL(blob);
        var a = document.createElement("a");
        a.href = downloadUrl;
        a.download = "data.xls";
        document.body.appendChild(a);
        a.click();
    } else {
        alert('Unable to download excel.')
    }
};
xhr.send(JSON.stringify(jsonData));

Hope this helps.

SMGhost
  • 3,867
  • 6
  • 38
  • 68
  • 1
    works for chrome/ff. For IE, use msSaveOrOpenBlob http://stackoverflow.com/questions/24007073/open-links-made-by-createobjecturl-in-ie11 – user2827377 Oct 31 '15 at 23:58
  • 2
    This works.. I have tested on Chrome.. on Safari it doesn't :( – vasanth Oct 20 '16 at 22:35
  • 1
    +1 for [this.response] how to get blob actually , i was trying like this window.URL.createObjectURL(xhttp.response) – Andi AR Jun 20 '17 at 17:01
  • Thank you! Shocking that this is not very simple. Seems like it would be common thing, but took me forever to find this. – Stephen McCormick Sep 19 '17 at 21:51
  • note that you must add to body for it to work on Firefox. In the answer: `document.body.appendChild(a)`...in my code i put `document.body && document.body.appendChild(link);` – Zachary Ryan Smith Jan 26 '18 at 02:42
  • For Firefox and maybe Safari the link must be added to the body and can be hidden: |`document.body.appendChild(link); link.setAttribute('type', 'hidden');` – gabrielgeo Feb 23 '18 at 06:49