从Ajax下载文件(类似)

5

我在GSP中有一个ajax调用:

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

这是我的控制器动作中的代码块:

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()

我通过$.Ajax方法从视图中传递的数据获取票务列表。然后,我将该数据格式化为CSV文件,并希望将其导出为CSV文件,但是没有任何反应。数据被发送到客户端,但由于内容协商不正确,因此没有可下载的文件。我错过了什么?

我尝试过类似以下的操作:

$.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');
    }
});

在控制器中,我生成了纯文本字符串,但这样得到的文件没有扩展名,这是不可接受的。我该怎么做呢?谢谢。

3
我建议创建一个隐藏的 iframe/form 并将数据提交到它,这样当服务器返回 CSV 数据时,下载将像正常情况下开始。 - Kevin B
4个回答

14

你不需要通过 ajax 来实现此操作。页面不会在文件下载时重定向。


window.location.href = "/ControllerName/DownloadAsCSV?json=" + jsonStr; 对我很有帮助。 - Juan Zamora

6
我猜测 url 属性应该被修正,因为你的引号是相互冲突的。
尝试使用以下内容:
$.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');
    }
});

1
无论如何,我必须同意James Kleeh和Gregg的意见,没有必要通过ajax调用来实现它。 - lucke84
2
为什么我的文件名不是tiketi.csv而是一个随机的名称? - Phoenix
@Phoenix,“tiketi.csv”是问题的一部分,不确定您在这里问什么? - lucke84

6
据我所知,不可能通过AJAX触发文件下载。 相反,您可以像这样操作(例如使用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');
}

您可能会遇到问题,因为这是一个GET请求:“security”或参数太多。 但在正常情况下,这应该不会出现问题。

你能验证一下这个是否有效吗?我喜欢这个答案,但是我应该怎么做才能让旋转器消失呢?目前旋转器显示出来了,文件也在下载,但就是这样。旋转器一直停留在屏幕上。 - Bmoe

1

我有点晚了,但在做一些研究时偶然发现了这个解决方案。这个解决方案有点长,并且是从这里回答的各种问题中拼凑而成的。我认为这是对这个问题的直接回答,并且我已经在一个应用程序中使其工作。

<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>

第一个函数是标准的ajax调用,从后端调用获取数据数组。接下来是创建csv文件。第二行调用第三个函数来格式化csv文件。第二个函数继续从csv创建过程中进行,最后创建一个动态链接,附加到dom并点击下载。看起来可能有点冗长,但可以与后端的标准ajax调用一起使用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接