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

问题描述 投票:5回答:3

我正在尝试对发送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 \“”)

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

谢谢,

javascript jquery json excel rest
3个回答
0
投票

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

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


0
投票

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


0
投票

这对Blob URLs来说实际上很容易。

首先,下载文件。我会在TypeScript中使用fetchasync / await(你总是可以使用promise链而不是async / await和/或XHR而不是fetch):

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

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

/**
 * 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);
}

Other Notes

  • fetch响应对象包含标头,因此您可以解析Content-Disposition以获取服务器所需的文件名。我在网上找到了几个很好的正则表达式,对此非常有用。里程可能会有所不同,但我建议为此创建一个函数,并将其与一些不错的单元测试绑定。
  • 这比尝试将当前位置设置为文件位置要好得多,因为它允许您在POST中包含更多详细信息,包括API密钥或类似的安全性,此外它还允许您干净地处理错误/异常,并知道操作何时完成(例如,在尝试离开用户仍在等待下载的页面时发出警告)。
  • Blob甚至支持切片数据,所以你可以通过获取单个切片(yay Content-Range!)并将它们组装成单个Blob并下载最终的blob来扩展它以下载大文件,同时为用户提供良好的加载进度指标!
  • 您可以像使用任何其他URL一样使用Blob URL。它们指向资源,因此您可以将该URL传递给img标记,其他库请求URL,src标记等。
  • @phamductri在服务器上提供临时文件可能非常危险!如果您需要使用该模式,您将需要使用表或查找来抽象文件名,因此用户无法控制实际的文件名或路径(使用指定目录中的UUID),并确保用户可以只下载他们生成的文件。您需要确保的一些事项如下(这不是一个全面的列表): 用户无法指定要保存的任意路径 他们可以保存您的数据库配置文件等。 用户无法指定要读取的任意路径 他们可以读取您的数据库配置文件等。 文件名不能冲突。 用户A生成名为“accounts_to_pay.csv”的文件。用户B同时生成一个具有相同名称的文件(恶意或意外),现在用户A正在支付用户B想要的任何人。
© www.soinside.com 2019 - 2024. All rights reserved.