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

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


当前回答

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

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

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

其他回答

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

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

您使用的服务器端语言是什么?在我的应用程序中,我可以通过在PHP的响应中设置正确的标题轻松地从AJAX调用下载文件:

设置报头服务器端

header("HTTP/1.1 200 OK");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

// The optional second 'replace' parameter indicates whether the header
// should replace a previous similar header, or add a second header of
// the same type. By default it will replace, but if you pass in FALSE
// as the second argument you can force multiple headers of the same type.
header("Cache-Control: private", false);

header("Content-type: " . $mimeType);

// $strFileName is, of course, the filename of the file being downloaded. 
// This won't have to be the same name as the actual file.
header("Content-Disposition: attachment; filename=\"{$strFileName}\""); 

header("Content-Transfer-Encoding: binary");
header("Content-Length: " . mb_strlen($strFile));

// $strFile is a binary representation of the file that is being downloaded.
echo $strFile;

这实际上会将浏览器“重定向”到这个下载页面,但正如@ahren已经在他的评论中说的,它不会导航离开当前页面。

这一切都是关于设置正确的头文件,所以我相信您会找到适合您所使用的服务器端语言(如果不是PHP的话)的解决方案。

处理响应客户端

假设您已经知道如何进行AJAX调用,那么在客户端向服务器执行AJAX请求。然后服务器生成一个可以下载该文件的链接,例如你想要指向的“转发”URL。 例如,服务器响应如下:

{
    status: 1, // ok
    // unique one-time download token, not required of course
    message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}

在处理响应时,你在你的主体中注入一个iframe,并将iframe的SRC设置为你刚刚接收到的URL,如下所示(使用jQuery来简化这个例子):

$("body").append("<iframe src='" + data.message +
  "' style='display: none;' ></iframe>");

如果您如上所示设置了正确的标题,iframe将强制下载对话框,而不需要将浏览器从当前页面导航出去。

Note

关于你的问题的额外补充;我认为使用AJAX技术请求东西时最好总是返回JSON。收到JSON响应后,就可以在客户端决定如何处理它。也许,例如,稍后您希望用户单击指向URL的下载链接,而不是强制直接下载,在您当前的设置中,您将不得不更新客户端和服务器端来实现这一点。

如果response是一个数组缓冲区,在Ajax的onsuccess事件下尝试这个:

 if (event.data instanceof ArrayBuffer) {
          var binary = '';
          var bytes = new Uint8Array(event.data);
          for (var i = 0; i < bytes.byteLength; i++) {
              binary += String.fromCharCode(bytes[i])
          }
          $("#some_id").append("<li><img src=\"data:image/png;base64," + window.btoa(binary) + "\"/></span></li>");
          return;
      }

事件的地方。数据是XHR事件成功函数接收到的响应。

还有另一种解决方案下载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");

    ...

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

为了让乔纳森赔罪的答案在边缘工作,我做了以下改变:

var blob = typeof File === 'function'
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

这个

var f = typeof File+"";
var blob = f === 'function' && Modernizr.fileapi
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

我宁愿把这篇文章作为评论发表,但我在这方面没有足够的声誉