JavaScript/jQuery 通过 POST 下载包含 JSON 数据的文件

2022-08-30 00:24:56

我有一个基于jquery的单页webapp。它通过 AJAX 调用与 RESTful Web 服务进行通信。

我正在尝试完成以下操作:

  1. 将包含 JSON 数据的 POST 提交到 REST URL。
  2. 如果请求指定了 JSON 响应,则返回 JSON。
  3. 如果请求指定了 PDF/XLS/etc 响应,则返回可下载的二进制文件。

我现在有1个和2个工作,客户端jquery应用程序通过基于JSON数据创建DOM元素在网页中显示返回的数据。我还有#3从Web服务的角度工作,这意味着如果给定正确的JSON参数,它将创建并返回一个二进制文件。但我不确定在客户端javascript代码中处理#3的最佳方法。

是否有可能从这样的ajax调用中取回可下载的文件?如何让浏览器下载并保存文件?

$.ajax({
    type: "POST",
    url: "/services/test",
    contentType: "application/json",
    data: JSON.stringify({category: 42, sort: 3, type: "pdf"}),
    dataType: "json",
    success: function(json, status){
        if (status != "success") {
            log("Error loading data");
            return;
        }
        log("Data loaded!");
    },
    error: function(result, status, err) {
        log("Error loading data");
        return;
    }
});

服务器使用以下标头进行响应:

Content-Disposition:attachment; filename=export-1282022272283.pdf
Content-Length:5120
Content-Type:application/pdf
Server:Jetty(6.1.11)

另一个想法是生成PDF并将其存储在服务器上,并返回包含文件URL的JSON。然后,在 ajax 成功处理程序中发出另一个调用以执行类似如下操作:

success: function(json,status) {
    window.location.href = json.url;
}

但这样做意味着我需要对服务器进行多次调用,并且我的服务器需要构建可下载的文件,将它们存储在某个地方,然后定期清理该存储区域。

必须有一种更简单的方法来实现这一目标。想法?


编辑:在查看了$.ajax的文档后,我看到响应dataType只能是其中之一,所以我猜没有办法使用ajax请求直接下载文件,除非我按照@VinayC答案中的建议使用Data URI方案嵌入二进制文件(这不是我想做的事情)。xml, html, script, json, jsonp, text

所以我想我的选择是:

  1. 不使用 ajax,而是提交表单帖子并将我的 JSON 数据嵌入到表单值中。可能需要弄乱隐藏的iframe等。

  2. 不要使用 ajax,而是将我的 JSON 数据转换为查询字符串,以构建标准的 GET 请求并将 window.location.href 设置为此 URL。可能需要在我的单击处理程序中使用 event.preventDefault() 以防止浏览器从应用程序 URL 更改。

  3. 使用我上面的其他想法,但通过@naikus答案的建议进行了增强。提交带有一些参数的 AJAX 请求,该参数让 Web 服务知道这是通过 ajax 调用调用的。如果从 ajax 调用调用 Web 服务,只需返回 JSON 以及所生成资源的 URL。如果直接调用资源,则返回实际的二进制文件。

我越想越喜欢最后一个选择。通过这种方式,我可以获取有关请求的信息(生成时间,文件大小,错误消息等),并且可以在开始下载之前对该信息执行操作。缺点是服务器上的额外文件管理。

还有其他方法可以做到这一点吗?我应该知道这些方法的任何优点/缺点?


答案 1

letronje的解决方案仅适用于非常简单的页面。 获取正文的 HTML 文本,追加 iframe HTML,并将页面的内部 HTML 设置为该字符串。这将清除您的页面具有的任何事件绑定,以及其他内容。创建一个元素并改用。document.body.innerHTML +=appendChild

$.post('/create_binary_file.php', postData, function(retData) {
  var iframe = document.createElement("iframe");
  iframe.setAttribute("src", retData.url);
  iframe.setAttribute("style", "display: none");
  document.body.appendChild(iframe);
}); 

或者使用jQuery

$.post('/create_binary_file.php', postData, function(retData) {
  $("body").append("<iframe src='" + retData.url+ "' style='display: none;' ></iframe>");
}); 

这实际上做了什么:使用变量postData中的数据执行/create_binary_file.php的帖子;如果该帖子成功完成,请将新的 iframe 添加到页面正文。假设来自 /create_binary_file.php 的响应将包含一个值“url”,该值是可以从中下载生成的 PDF/XLS/etc 文件的 URL。将 iframe 添加到引用该 URL 的页面将导致浏览器提升用户下载该文件,前提是 Web 服务器具有适当的 mime 类型配置。


答案 2

我一直在尝试使用另一个使用blob的选项。我已经设法让它下载文本文档,并且我已经下载了PDF(但是它们已损坏)。

使用 Blob API,你将能够执行以下操作:

$.post(/*...*/,function (result)
{
    var blob=new Blob([result]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="myFileName.txt";
    link.click();

});

这是IE 10 +,Chrome 8 +,FF 4 +。查看 https://developer.mozilla.org/en-US/docs/Web/API/URL.createObjectURL

它只会在Chrome,Firefox和Opera中下载文件。这将使用锚点标记上的下载属性来强制浏览器下载它。