10

I have got a problem with getting excel file and opening download window in the browser after getting a response (in success ajax method) with that file. I have got appropriate Content-Type and Content-Disposition headers, I tried using Blob in js and I couldn't achieve what I want - simple file downloading.
I accomplished few versions of my ajax, one of them is below. I developed ajax which returns excel file which I couldn't open properly because it's corrupted (despite .xlsx extension).

Maybe the problem is with inappropriate data type used in Blob constructor?

I tried using "xhr.response" instead of "data" from success method arguments but it doesn't work too. I checked Response Headers in Developer Tools in Chrome and they are set properly.
The important thing - all the excel workbooks created on the server side is correct because it worked in the previous version when data was sent in URL, not in ajax post.

Controller method in Java/Spring server side below:

response.reset();
response.setContentType("application/vnd.ms-excel");
response.addHeader("Content-Disposition","attachment;filename=\"" + className + " " +  title + ".xlsx\"");
    try (ServletOutputStream output = response.getOutputStream()){
        workbook.write(output);
        output.flush();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

My Ajax to download file and open download window:

$.ajax({
    url: myUrl,
    type: 'POST',
    data: myData,
    success: function(data, status, xhr) {
        var contentType = 'application/vnd.ms-excel';

        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) {
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        }
        console.log("FILENAME: " + filename);

        try {
            var blob = new Blob([data], { type: contentType });

            var downloadUrl = URL.createObjectURL(blob);
            var a = document.createElement("a");
            a.href = downloadUrl;
            a.download = filename;
            document.body.appendChild(a);
            a.click();

        } catch (exc) {
            console.log("Save Blob method failed with the following exception.");
            console.log(exc);
        }
Abijith Mg
  • 2,647
  • 21
  • 35
KamilosD
  • 333
  • 1
  • 2
  • 12

3 Answers3

21

It looks like JQuery have got some problem with dealing with the binary data from the response. I used simply XMLHttpRequest and I add all data to the URL.

var request = new XMLHttpRequest();
request.open('POST', url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
request.responseType = 'blob';

request.onload = function(e) {
    if (this.status === 200) {
        var blob = this.response;
        if(window.navigator.msSaveOrOpenBlob) {
            window.navigator.msSaveBlob(blob, fileName);
        }
        else{
            var downloadLink = window.document.createElement('a');
            var contentTypeHeader = request.getResponseHeader("Content-Type");
            downloadLink.href = window.URL.createObjectURL(new Blob([blob], { type: contentTypeHeader }));
            downloadLink.download = fileName;
            document.body.appendChild(downloadLink);
            downloadLink.click();
            document.body.removeChild(downloadLink);
           }
       }
   };
   request.send();
KamilosD
  • 333
  • 1
  • 2
  • 12
  • Oh my god thank you soo much for this answer. You are the best! – Sol May 12 '18 at 19:06
  • `fileName` on line 16 has not been defined. Where do you define that? – Möoz Dec 17 '18 at 22:18
  • 1
    @Möoz It's set up before. But in this case it doesn't matter because it could be anything like "My_File.xlsx" – KamilosD Dec 18 '18 at 10:11
  • i am able to download the file but when i open it it says "Sorry,we couldn't find C:\mypath\[object Object],[object Object],....[object Ob.xls. Is it possible it was moved, renamed or deleted?" Also is there anyway to change the name of the file during download? – Diamond King Feb 18 '19 at 08:08
3

After so many searches for getting an excel file from web API with Unicode content. Finally, this code works for me :

$.ajax({
                type: 'GET',
                cache: false,
                url: "https://localhost:44320/WeatherForecast",
              
                xhrFields: {
                    // make sure the response knows we're expecting a binary type in return.
                    // this is important, without it the excel file is marked corrupted.
                    responseType: 'arraybuffer'
                }
            })
                .done(function (data, status, xmlHeaderRequest) {
                    var downloadLink = document.createElement('a');
                    var blob = new Blob([data],
                        {
                            type: xmlHeaderRequest.getResponseHeader('Content-Type')
                        });
                    var url = window.URL || window.webkitURL;
                    var downloadUrl = url.createObjectURL(blob);
                    var fileName = '';

                  

                    if (typeof window.navigator.msSaveBlob !== 'undefined') {
                        window.navigator.msSaveBlob(blob, fileName);
                    } else {
                        if (fileName) {
                            if (typeof downloadLink.download === 'undefined') {
                                window.location = downloadUrl;
                            } else {
                                downloadLink.href = downloadUrl;
                                downloadLink.download = fileName;
                                document.body.appendChild(downloadLink);
                                downloadLink.click();
                            }
                        } else {
                            window.location = downloadUrl;
                        }

                        setTimeout(function () {
                            url.revokeObjectURL(downloadUrl);
                        },
                            100);
                    }
                });

Rostam Bamasi
  • 204
  • 1
  • 6
1

We were having absolutely the same trouble recently. For us it started to work when we add responseType: 'arraybuffer' to the ajax parameters. And it's better to use lib https://github.com/eligrey/FileSaver.js/ instead of manual clicking on the link because this tool revokes memory as well.

Aniruddha Das
  • 20,520
  • 23
  • 96
  • 132
  • I have tried with `responseType: 'arraybuffer'` but it failed because of the inappropriate responseType which was `text` probably. But I resolved this problem with directly creating XMLHttpRequest. Working code in the comment below. – KamilosD Nov 09 '17 at 09:13