5

I have this ajax call in my GSP:

$.ajax({
    url: '${request.contextPath + '/Ticket/passAll'}',
    type: 'POST',
    data: data,
    success: function() {
        alert("Success");
    }
});

This is code block from my controller action:

response.setHeader("Content-disposition", "attachment; filename=sample.csv")
response.contentType = "application/vnd.ms-excel"

def outs = response.outputStream
def cols = [:]

tickets.each() {
    outs << it.ticketNo + ";" + it.subject
    outs << "\n"
}

outs.flush()
outs.close()

I get tickets list from data that I pass from view via $.Ajax method. Than I format that data as CSV and than I want to export that data as CSV file but nothing happens. The data is send to client but there is no file for download as content-disposition is not well formed. What am I missing? I've tried to do something like:

$.ajax({
    url: '${request.contextPath + '/Ticket/passAll'}',
    type: 'POST',
    data: aoData,
    dataType: 'text',
    success: function(result) {
        var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(result);
        window.open(uri, 'tiketi.csv');
    }
});

and in controller I generate plain string, but that way I get a file without extension which is unacceptable.

How can I achieve this? Thank you.

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
drago
  • 1,207
  • 4
  • 24
  • 45
  • 3
    I'd suggest instead creating a hidden iframe/form and posting to it, that way when your server returns the csv, the download will begin like normal. – Kevin B Jan 16 '13 at 15:25

4 Answers4

14

You dont need to do it via ajax. The page will not redirect for file downloads.

James Kleeh
  • 12,094
  • 5
  • 34
  • 61
6

I guess the url property should be fixed as your quotes are colliding.

Try with:

$.ajax({
    url: "${request.contextPath}/Ticket/passAll",
    type: 'POST',
    data: aoData,
    dataType: 'text',
    success: function(result) {
        var uri = 'data:application/csv;charset=UTF-8,' + encodeURIComponent(result);
        window.open(uri, 'tiketi.csv');
    }
});
lucke84
  • 4,516
  • 3
  • 37
  • 58
6

As far as I know, it's not possible to trigger file-downloads via AJAX. Instead you may do something like this (e.g. with jQuery):

function downloadComplete(){ hideSpinner();}
function downloadStart(){ showSpinner();}

function download(){
    downloadStart()
    var urlParams = data // or e.g. $(form).serialize() 
    var downloadUrl = "${request.contextPath}/Ticket/passAll?" + urlParams 
    $("#download-iframe").remove()
    $('<iframe style="display:none" id="download-iframe" src="' + downloadUrl + '" onreadystatechange="downloadComplete()" onload="downloadComplete()">').appendTo('body');
}

You may have problems because it's a GET-request: "security" or too many arguments. But in normal it should be no problem.

Martin L.
  • 3,006
  • 6
  • 36
  • 60
  • Can you verify that this works? I like this answer, but what am I supposed to do to make the spinner go away? Right now the spinner shows and the file downloads but that's it. Spinner stays on screen. – Bmoe Apr 10 '18 at 13:59
1

Im a little late on this one but stumbled upon it doing some research. This solution is a bit longer and cobbled together from various questions answered here. I think its a direct answer to this question and I have it working in an application.

<script>
function getCSV() {
console.log("in get csv");
  $.ajax({
      url: "<url string>",
      dataType: 'json',
      type: "post",
      data: {},
      success: function (data) {
          console.log(data);
          downloadCSV({filename: "<filename.csv", data: data.<array from 
                                      backend call>});
      },
      error: function () {
      }
  });
}
}
function downloadCSV(args) {
var data, filename, link;
var csv = convertArrayOfObjectsToCSV({
    data: args.data
});
if (csv == null) return;
filename = args.filename || 'export.csv';
if (!csv.match(/^data:text\/csv/i)) {
    csv = 'data:text/csv;charset=utf-8,' + csv;
}
data = encodeURI(csv);
link = document.createElement('a');
link.setAttribute('href', data);
link.setAttribute('download', filename);
link.click();
}
function convertArrayOfObjectsToCSV(args) {
var result, ctr, keys, columnDelimiter, lineDelimiter, data;
data = args.data || null;
if (data == null || !data.length) {
    return null;
}
columnDelimiter = args.columnDelimiter || ',';
lineDelimiter = args.lineDelimiter || '\n';
keys = Object.keys(data[0]);
result = '';
result += keys.join(columnDelimiter);
result += lineDelimiter;
data.forEach(function (item) {
    ctr = 0;
    keys.forEach(function (key) {
        if (ctr > 0) result += columnDelimiter;
        result += item[key];
        ctr++;
    });
    result += lineDelimiter;
});
return result;
}
</script>

The first function is a standard ajax call to get the array of data from a backend call. The next is to create the csv. The second line calls a third function that formats the csv file. The second function moves on from the csv creation and finally creates a dynamic link that is appended to the dom and clicked for the download. Seems the longer way round but works with standard ajax call to the backend.

sixstring
  • 262
  • 4
  • 10