向服务器发出JSON POST请求,接收二进制响应(Excel文件),如何下载?

时间:2014-02-10 05:16:44

标签: javascript jquery json excel rest

我正在尝试对发送JSON数据的服务器进行POST调用。服务器获取JSON数据,进行一些处理,然后发回Excel .xlsx作为响应。我希望浏览器打开“将文件另存为”对话框以供用户保存。我一直在寻找一个干净的解决方案来做到这一点。但是这个问题JavaScript/jQuery to download file via POST with JSON data中的一个可能的解决方案建议将Excel文件保存在服务器上然后发回URL链接,然后打开iframe供用户下载。这对我来说是不行的,因为用户可以在服务器上创建数千个Excel文件,并且服务器具有有限的节省空间。我希望解决方案能够即时实现。我见过的另一个解决方案建议将数据转换为表单,然后使用表单提交。再次这是一个禁忌,因为我的数据在数百甚至数千个Excel行的范围内。

我的jQuery POST调用:

   $.ajax({
          type: 'POST',
          url: '/server/path',
            data: JSON.stringify(dataSent),
            processData: false,
            success: function(data, textStatus, jqXHR) {

            },
            error: function(result, status, err) {

            },
            contentType: 'application/json',
            dataType: 'application/vnd.ms-excel'
        }); 

在后端我设置了这个:

Response.header(“Content-Type”,“application / vnd.ms-excel”)

Response.header(“Content-Disposition”,“attachment; filename = \”export.xlsx \“”)

强制浏览器打开“将文件另存为...”对话框的最佳方法是什么?

谢谢,

3 个答案:

答案 0 :(得分:0)

我不确定是否有办法通过JS接收二进制数据然后启动下载。

如果我的任务是,我会将方法更改为GET并生成文件(作为流)并使用适当的标头返回它(Content-Disposition,Content-Length,Content-Type)

答案 1 :(得分:0)

我找到了解决这个问题的方法。我没有进行POST调用来强制浏览器打开保存对话框,而是进行POST调用以生成文件,然后将文件临时存储在服务器上,返回文件名。然后使用“Content-Disposition:attachment; filename = filename1”对此文件使用GET调用。使用该标头的GET调用将强制浏览器始终打开“保存此文件”对话框。

答案 2 :(得分:0)

Blob URLs实际上很容易。

首先,下载文件。我会在TypeScript中使用fetchasync / await(您可以始终使用保证链而不是async / await和/或XHR而不是{ {1}}):

fetch

现在你有了一个blob,你可以通过创建一个Blob URL和一个隐藏的链接将它传递给一个函数来下载它:

(async () => {
   let response = await fetch("/post/path", {
       body: JSON.stringify(data), // must match 'Content-Type' header
       headers: {
           'content-type': 'application/json'
       },
       method: 'POST',
   });

   let blob = await response.blob();
   let filename = "file.txt";

   saveBlobAsFile(filename, blob); // This function is defined below
})();

其他注释

  • /** * Downloads a blob as a file. * * TODO: Support iOS Safari, which doesn't support the "download" attribute. * * @param name The name of the downloaded file * @param blob The blob to download */ export function saveBlobAsFile(name: string, blob: Blob) { // IE10 & IE11 Support, since they don't support the "download" // attribute on anchor tags. if (navigator.msSaveBlob) { navigator.msSaveBlob(blob, name); return; } // Create the URL and hidden anchor tag let $hiddenAnchorTag = $('<a style="display: none;"/>'); let url = URL.createObjectURL(blob); // Set the attributes for the download $hiddenAnchorTag.attr('href', url); $hiddenAnchorTag.attr('download', name); // Insert the link and click to download $(document.body).append($hiddenAnchorTag); $hiddenAnchorTag[0].click(); // Clean up after ourselves $hiddenAnchorTag.remove(); URL.revokeObjectURL(url); } 响应对象包含标头,因此您可以解析Content-Disposition以获取服务器所需的文件名。我在网上找到了几个很好的正则表达式,对此非常有用。里程可能会有所不同,但我建议为此制作一个功能,并将其与一些不错的单元测试绑定。
  • 这比尝试将当前位置设置为文件位置要好得多,因为它允许您在POST中包含更多详细信息,包括API密钥或类似的安全性,此外它还允许您处理错误/ exception干净,并知道操作何时完成(例如在尝试离开用户仍在等待下载的页面时发出警告)。
  • Blob甚至支持切片数据,所以你可以通过获取单个切片(yay Content-Range !)并将它们组装成单个Blob并下载最终文件来扩展它以下载大文件blob,同时为用户提供了一个很好的加载进度指示器!
  • 您可以像使用任何其他网址一样使用Blob网址。它们指向资源,因此您可以将该URL传递给img标记,其他库请求URL,src标记等。
  • @phamductri在服务器上提供临时文件可能非常危险!如果您需要使用该模式,您将需要使用表或查找来抽象文件名,因此用户无法控制实际的文件名或路径(使用指定目录中的UUID),并确保用户可以只下载他们生成的文件。您需要确保的一些事项如下(这不是一个全面的列表):
    • 用户无法指定要保存的任意路径
      • 他们可以保存您的数据库配置文件等。
    • 用户无法指定要读取的任意路径
      • 他们可以读取您的数据库配置文件等。
    • 文件名不能冲突。
      • 用户A生成名为“accounts_to_pay.csv”的文件。用户B同时生成一个具有相同名称的文件(恶意或意外),现在用户A正在支付用户B想要的任何人。