我有一个javascript应用程序,发送ajax POST请求到某个URL。Response可以是JSON字符串,也可以是文件(作为附件)。我可以很容易地在ajax调用中检测到Content-Type和Content-Disposition,但是一旦我检测到响应包含一个文件,我如何让客户端下载它呢?我在这里读过一些类似的帖子,但没有一个能提供我想要的答案。

请,请,请不要发布建议我不应该使用ajax或我应该重定向浏览器的答案,因为这些都不是一个选项。使用普通的HTML表单也是不可行的。我所需要的是向客户端显示一个下载对话框。这能做到吗?如何做到?


当前回答

还有另一种解决方案下载ajax网页。但我指的是必须首先处理然后下载的页面。

首先,您需要将页面处理与结果下载分离。

1) ajax调用中只进行页面计算。

$.post("CalculusPage.php", { calculusFunction: true, ID: 29, data1: "a", data2: "b" },

       function(data, status) 
       {
            if (status == "success") 
            {
                /* 2) In the answer the page that uses the previous calculations is downloaded. For example, this can be a page that prints the results of a table calculated in the ajax call. */
                window.location.href = DownloadPage.php+"?ID="+29;
            }               
       }
);

// For example: in the CalculusPage.php

    if ( !empty($_POST["calculusFunction"]) ) 
    {
        $ID = $_POST["ID"];

        $query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID;
        ...
    }

// For example: in the DownloadPage.php

    $ID = $_GET["ID"];

    $sede = "SELECT * FROM ExamplePage WHERE id = ".$ID;
    ...

    $filename="Export_Data.xls";
    header("Content-Type: application/vnd.ms-excel");
    header("Content-Disposition: inline; filename=$filename");

    ...

我希望这个解决方案对很多人都有用,就像对我一样。

其他回答

我想指出在接受的答案中使用这种技术时出现的一些困难,即使用表单post:

You can't set headers on the request. If your authentication schema involves headers, a Json-Web-Token passed in the Authorization header, you'll have to find other way to send it, for example as a query parameter. You can't really tell when the request has finished. Well, you can use a cookie that gets set on response, as done by jquery.fileDownload, but it's FAR from perfect. It won't work for concurrent requests and it will break if a response never arrives. If the server responds with a error, the user will be redirected to the error page. You can only use the content types supported by a form. Which means you can't use JSON.

我最终使用在S3上保存文件并发送预签名URL来获取文件的方法。

创建一个表单,使用POST方法,提交表单-不需要iframe。当服务器页面响应请求时,为文件的mime类型写一个响应头,它将呈现一个下载对话框——我已经做过很多次了。

你想要内容类型的应用程序/下载-只要搜索如何为你使用的任何语言提供下载。

正如其他人所述,您可以通过POST请求创建并提交表单来下载。但是,您不必手动执行此操作。

有一个非常简单的库可以做到这一点,那就是jquery.redirect。它提供了一个类似于标准jQuery的API。post方法:

$.redirect(url, [values, [method, [target]]])

我使用这个FileSaver.js。在我的情况下,与csv文件,我这样做(在coffescript):

  $.ajax
    url: "url-to-server"
    data: "data-to-send"
    success: (csvData)->
      blob = new Blob([csvData], { type: 'text/csv' })
      saveAs(blob, "filename.csv")

我认为对于大多数复杂的情况,必须对数据进行正确的处理。在底层,filesver .js实现了与Jonathan Amend的答案相同的方法。

下面是我使用临时隐藏表单的解决方案。

//Create an hidden form
var form = $('<form>', {'method': 'POST', 'action': this.href}).hide();

//Add params
var params = { ...your params... };
$.each(params, function (k, v) {
    form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v}));
});

//Make it part of the document and submit
$('body').append(form);
form.submit();

//Clean up
form.remove();

注意,我大量使用JQuery,但你可以用原生JS做同样的事情。