处理从 ajax post 下载的文件

Posted

技术标签:

【中文标题】处理从 ajax post 下载的文件【英文标题】:Handle file download from ajax post 【发布时间】:2013-04-11 18:10:34 【问题描述】:

我有一个 javascript 应用程序,可以将 ajax POST 请求发送到某个 URL。响应可能是 JSON 字符串,也可能是文件(作为附件)。我可以在我的 ajax 调用中轻松检测到 Content-Type 和 Content-Disposition,但是一旦我检测到响应包含一个文件,我该如何让客户端下载它?我在这里阅读了许多类似的主题,但没有一个提供我正在寻找的答案。

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

【问题讨论】:

看过这篇文章的朋友请看这篇文章:***.com/questions/20830309/… 我已从问题中删除了您的解决方案。欢迎您将其发布为下面的答案帖子,但它不属于问题帖子。 【参考方案1】:

不要这么快放弃,因为(在现代浏览器中)可以使用部分 FileAPI 来完成:

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'blob';
xhr.onload = function () 
    if (this.status === 200) 
        var blob = this.response;
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) 
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        

        if (typeof window.navigator.msSaveBlob !== 'undefined') 
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
         else 
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) 
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') 
                    window.location.href = downloadUrl;
                 else 
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                
             else 
                window.location.href = downloadUrl;
            

            setTimeout(function ()  URL.revokeObjectURL(downloadUrl); , 100); // cleanup
        
    
;
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params, true));

或者如果使用 jQuery.ajax:

$.ajax(
    type: "POST",
    url: url,
    data: params,
    xhrFields: 
        responseType: 'blob' // to avoid binary data being mangled on charset conversion
    ,
    success: function(blob, status, xhr) 
        // check for a filename
        var filename = "";
        var disposition = xhr.getResponseHeader('Content-Disposition');
        if (disposition && disposition.indexOf('attachment') !== -1) 
            var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
            var matches = filenameRegex.exec(disposition);
            if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
        

        if (typeof window.navigator.msSaveBlob !== 'undefined') 
            // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
            window.navigator.msSaveBlob(blob, filename);
         else 
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) 
                // use HTML5 a[download] attribute to specify filename
                var a = document.createElement("a");
                // safari doesn't support this yet
                if (typeof a.download === 'undefined') 
                    window.location.href = downloadUrl;
                 else 
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                
             else 
                window.location.href = downloadUrl;
            

            setTimeout(function ()  URL.revokeObjectURL(downloadUrl); , 100); // cleanup
        
    
);

【讨论】:

【参考方案2】:

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

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

【讨论】:

如问题所述:“使用纯 HTML 表单也不是一种选择。” 否,因为使用常规 POST 会将浏览器导航到 POST URL。我不想离开页面。我想在后台执行请求,处理响应并将其呈现给客户端。 如果服务器像其他答案一样发回标头,它会在新窗口中打开 - 我以前做过。如果您的服务器端脚本返回 HTML 代码,它只会导航离开 @PavlePredic 您是否最终弄清楚如何管理这两种响应场景,即 JSON 文本响应或下载文件响应? 答案不明确,建议的解决方案不起作用。【参考方案3】:

我遇到了同样的问题并成功解决了。我的用例是这样的。

"将 JSON 数据发送到服务器并接收一个 excel 文件。 该 excel 文件由服务器创建并作为对客户端的响应返回。在浏览器中将该响应下载为具有自定义名称的文件

$("#my-button").on("click", function()

// Data to post
data = 
    ids: [1, 2, 3, 4, 5]
;

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() 
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) 
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    
;
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
);

上面的sn-p只是在做后续

使用 XMLHttpRequest 将数组作为 JSON 发布到服务器。 在以 blob(二进制)形式获取内容后,我们将创建一个可下载的 URL 并将其附加到不可见的“a”链接,然后单击它。

这里我们需要在服务器端仔细设置一些东西。我在 Python Django HttpResponse 中设置了一些标头。如果您使用其他编程语言,则需要相应地设置它们。

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

由于我在这里下载 xls(excel),所以我将 contentType 调整为上述一个。您需要根据您的文件类型进行设置。您可以使用此技术下载任何类型的文件。

【讨论】:

【参考方案4】:

您使用什么服务器端语言?在我的应用程序中,我可以通过在 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 将强制一个下载对话框,而不会使浏览器离开当前页面。

注意

与您的问题相关的额外补充;我认为使用 AJAX 技术请求内容时最好总是返回 JSON。收到 JSON 响应后,您可以决定客户端如何处理它。例如,也许稍后您希望用户单击 URL 的下载链接而不是直接强制下载,在您当前的设置中,您必须同时更新客户端和服务器端才能这样做。

【讨论】:

【参考方案5】:

这就是我的工作方式 https://***.com/a/27563953/2845977

$.ajax(
  url: '<URL_TO_FILE>',
  success: function(data) 
    var blob=new Blob([data]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
    link.click();
  
);

使用download.js 更新了答案

$.ajax(
  url: '<URL_TO_FILE>',
  success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
);

【讨论】:

嗨,我需要 jQuery 3.0 > 才能工作吗? 我还得到了一个空白 pdf,里面有你给出的两个例子。我正在尝试让它下载一个 pdf 文件。 @gbade_ 不,您不需要任何特定的 jQuery 版本。应该适用于所有版本。您是否检查了您正在下载的 PDF 是否具有正确的 CORS 标头?控制台上的任何错误都可能有助于调试 这对我使用 download.js 有用:success: function (response, status, request) download(response, "filename.txt", "application/text"); 【参考方案6】:

对于那些从 Angular 角度寻找解决方案的人来说,这对我有用:

$http.post(
  'url',
  ,
  responseType: 'arraybuffer'
).then(function (response) 
  var headers = response.headers();
  var blob = new Blob([response.data],type:headers['content-type']);
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = "Filename";
  link.click();
);

【讨论】:

这有帮助,但我需要保留原始文件名。我在“Content-Disposition”下的响应标头中看到了文件名,但在代码的响应对象中找不到该值。设置link.download = "" 会产生一个随机的guid 文件名,而link.download = null 会产生一个名为“null”的文件。 @Marie,您可以使用INPUT 元素的HTMLInputElement.files 属性记录上传时的文件名。请参阅The MDN docs on the file input 了解更多详情。 Blob 大小有限:***.com/questions/28307789/…【参考方案7】:

对于那些寻求更现代方法的人,您可以使用fetch API。以下代码显示了如何下载电子表格文件。

fetch(url, 
    body: JSON.stringify(data),
    method: 'POST',
    headers: 
        'Content-Type': 'application/json; charset=utf-8'
    ,
)
.then(response => response.blob())
.then(response => 
    const blob = new Blob([response], type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.xlsx";
    document.body.appendChild(a);
    a.click();
)

我相信这种方法比其他XMLHttpRequest 解决方案更容易理解。此外,它的语法与jQuery 方法相似,无需添加任何额外的库。

当然,我建议检查您正在开发的浏览器,因为这种新方法不适用于 IE。您可以在以下 [链接][1] 上找到完整的浏览器兼容性列表。

重要提示:在此示例中,我将 JSON 请求发送到侦听给定 url 的服务器。这个url 必须设置,在我的例子中我假设你知道这部分。此外,请考虑您的请求工作所需的标头。由于我发送的是 JSON,所以我必须添加 Content-Type 标头并将其设置为 application/json; charset=utf-8,以便让服务器知道它将接收的请求类型。

【讨论】:

太棒了!要在新选项卡而不是下载弹出窗口中打开它:使用 ``` const window = open(downloadUrl, "_blank"); if (window !== null) window.focus(); ``` 我有办法对多组数据执行此操作吗?例如,从 api 调用中取回 fileOne: data, fileTwo: data, fileThree: data 并一次生成三个下载的文件?谢谢! 嗯,我没试过。但是您始终可以将图像压缩为 zip 文件并下载。我会检查是否可以。【参考方案8】:

我看到您已经找到了解决方案,但是我只是想添加一些信息,这些信息可能有助于尝试通过大型 POST 请求实现相同目标的人。

几周前我遇到了同样的问题,确实不可能通过 AJAX 实现“干净”下载,Filament Group 创建了一个 jQuery 插件,它的工作方式与您已经发现的完全一样,它是称为jQuery File Download,但是这种技术有一个缺点。

如果您通过 AJAX 发送大请求(例如文件 +1MB),则会对响应速度产生负面影响。在缓慢的 Internet 连接中,您将不得不等待 很多 直到发送请求并等待文件下载。它不像即时的“点击”=>“弹出”=>“下载开始”。它更像是“单击”=>“等待数据发送”=>“等待响应”=>“下载开始”,这使得文件看起来是两倍大小,因为您必须等待发送请求通过 AJAX 并将其作为可下载文件取回。

如果您使用

我的app允许用户导出动态生成的图片,这些图片通过POST请求以base64格式发送到服务器(这是唯一可能的方式),然后处理后以.png、.jpg的形式发回给用户文件,图像 +1MB 的 base64 字符串很大,这迫使用户等待文件开始下载的必要时间。在互联网连接速度较慢的情况下,这真的很烦人。

我的解决方案是临时将文件写入服务器,一旦准备好,以按钮的形式动态生成指向文件的链接,该按钮在“请稍候...”和“下载”状态之间切换,并且同时,在预览弹出窗口中打印 base64 图像,以便用户“右键单击”并保存。这使得所有等待时间对用户来说更容易忍受,也加快了速度。

2014 年 9 月 30 日更新:

自从我发布这篇文章以来已经过去了几个月,我终于找到了一种更好的方法来加快处理大型 base64 字符串的速度。我现在将 base64 字符串存储到数据库中(使用 longtext 或 longblog 字段),然后我通过 jQuery File Download 传递它的记录 ID,最后在下载脚本文件上我使用此 ID 查询数据库以提取 base64 字符串并传递它下载功能。

下载脚本示例:

<?php
// Record ID
$downloadID = (int)$_POST['id'];
// Query Data (this example uses CodeIgniter)
$data       = $CI->MyQueries->GetDownload( $downloadID );
// base64 tags are replaced by [removed], so we strip them out
$base64     = base64_decode( preg_replace('#\[removed\]#', '', $data[0]->image) );
// This example is for base64 images
$imgsize    = getimagesize( $base64 );
// Set content headers
header('Content-Disposition: attachment; filename="my-file.png"');
header('Content-type: '.$imgsize['mime']);
// Force download
echo $base64;
?>

我知道这超出了 OP 的要求,但是我觉得用我的发现更新我的答案会很好。当我在寻找我的问题的解决方案时,我阅读了很多 “从 AJAX POST 数据下载” 线程,这些线程并没有给出我正在寻找的答案,我希望这些信息可以帮助那些想要实现这样的目标。

【讨论】:

jQuery File Download 仅将我重定向到 url。我这样称呼它:jQuery.download("api/ide/download-this-file.php", filePath: path2Down, "POST");.【参考方案9】:

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

    您不能在请求上设置标头。如果您的身份验证架构涉及标头,即在 Authorization 标头中传递的 Json-Web-Token,则您必须找到其他方式来发送它,例如作为查询参数。

    您无法确定请求何时完成。好吧,您可以使用在响应时设置的 cookie,正如 jquery.fileDownload 所做的那样,但这远非完美。它不适用于并发请求,如果响应从未到达,它将中断。

    如果服务器响应错误,用户将被重定向到错误页面。

    您只能使用form 支持的内容类型。这意味着你不能使用 JSON。

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

【讨论】:

【参考方案10】:

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

//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。

【讨论】:

【参考方案11】:

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

一个非常简单的库就是jquery.redirect。它提供了一个类似于标准jQuery.post 方法的API:

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

【讨论】:

【参考方案12】:

这是一个 3 年前的问题,但我今天遇到了同样的问题。我查看了您编辑的解决方案,但我认为它会牺牲性能,因为它必须提出双重请求。因此,如果有人需要另一种不意味着两次调用服务的解决方案,那么这就是我这样做的方式:

<form id="export-csv-form" method="POST" action="/the/path/to/file">
    <input type="hidden" name="anyValueToPassTheServer" value="">
</form>

这个表单只是用来调用服务,避免使用window.location()。之后,您只需从 jquery 提交表单即可调用服务并获取文件。这很简单,但这样您就可以使用 POST 进行下载。我现在认为,如果您调用的服务是 GET,这可能会更容易,但我的情况并非如此。

【讨论】:

【参考方案13】:

我用过这个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")

我认为对于最复杂的情​​况,必须正确处理数据。在后台 FileSaver.js 实现与Jonathan Amend 的答案相同的方法。

【讨论】:

..但是你可以在ios中下载文件吗?【参考方案14】:

见:http://www.henryalgus.com/reading-binary-files-using-jquery-ajax/ 它会返回一个 blob 作为响应,然后可以将其放入文件保护程序中

【讨论】:

虽然理论上这可以回答这个问题,it would be preferable 在这里包含答案的基本部分,并提供链接以供参考。【参考方案15】:

为了让 Jonathan Amends answer 在 Edge 中工作,我进行了以下更改:

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

我宁愿将此作为评论发布,但我没有足够的声誉

【讨论】:

【参考方案16】:

这是我从不同来源收集的解决方案: 服务器端实现:

    String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
    // Set headers
    response.setHeader("content-disposition", "attachment; filename =" + fileName);
    response.setContentType(contentType);
    // Copy file to output stream
    ServletOutputStream servletOutputStream = response.getOutputStream();
    try (InputStream inputStream = new FileInputStream(file)) 
        IOUtils.copy(inputStream, servletOutputStream);
     finally 
        servletOutputStream.flush();
        Utils.closeQuitely(servletOutputStream);
        fileToDownload = null;
    

客户端实现(使用jquery):

$.ajax(
type: 'POST',
contentType: 'application/json',
    url: <download file url>,
    data: JSON.stringify(postObject),
    error: function(XMLHttpRequest, textStatus, errorThrown) 
        alert(errorThrown);
    ,
    success: function(message, textStatus, response) 
       var header = response.getResponseHeader('Content-Disposition');
       var fileName = header.split("=")[1];
       var blob = new Blob([message]);
       var link = document.createElement('a');
       link.href = window.URL.createObjectURL(blob);
       link.download = fileName;
       link.click();
    
);   

【讨论】:

【参考方案17】:

以下是我根据包含一些 id 的列表并在数据库中查找来下载多个文件的解决方案,文件将被确定并准备好下载 - 如果存在的话。 我正在使用 Ajax 为每个文件调用 C# MVC 操作。

是的,就像其他人所说的那样,可以在 jQuery Ajax 中实现。 我通过 Ajax 成功做到了,我总是发送响应 200。

所以,这是关键:

  success: function (data, textStatus, xhr) 

这是我的代码:

var i = 0;
var max = 0;
function DownloadMultipleFiles() 
            if ($(".dataTables_scrollBody>tr.selected").length > 0) 
                var list = [];
                showPreloader();
                $(".dataTables_scrollBody>tr.selected").each(function (e) 
                    var element = $(this);
                    var orderid = element.data("orderid");
                    var iscustom = element.data("iscustom");
                    var orderlineid = element.data("orderlineid");
                    var folderPath = "";
                    var fileName = "";

                    list.push( orderId: orderid, isCustomOrderLine: iscustom, orderLineId: orderlineid, folderPath: folderPath, fileName: fileName );
                );
                i = 0;
                max = list.length;
                DownloadFile(list);
            
        

然后调用:

function DownloadFile(list) 
        $.ajax(
            url: '@Url.Action("OpenFile","OrderLines")',
            type: "post",
            data: list[i],
            xhrFields: 
                responseType: 'blob'
            ,
            beforeSend: function (xhr) 
                xhr.setRequestHeader("RequestVerificationToken",
                    $('input:hidden[name="__RequestVerificationToken"]').val());

            ,
            success: function (data, textStatus, xhr) 
                // check for a filename
                var filename = "";
                var disposition = xhr.getResponseHeader('Content-Disposition');
                if (disposition && disposition.indexOf('attachment') !== -1) 
                    var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches != null && matches[1]) filename = matches[1].replace(/['"]/g, '');
                    var a = document.createElement('a');
                    var url = window.URL.createObjectURL(data);
                    a.href = url;
                    a.download = filename;
                    document.body.append(a);
                    a.click();
                    a.remove();
                    window.URL.revokeObjectURL(url);
                
                else 
                    getErrorToastMessage("Production file for order line " + list[i].orderLineId + " does not exist");
                
                i = i + 1;
                if (i < max) 
                    DownloadFile(list);
                
            ,
            error: function (XMLHttpRequest, textStatus, errorThrown) 

            ,
            complete: function () 
                if(i===max)
                hidePreloader();
            
        );
    

C#MVC:

 [HttpPost]
 [ValidateAntiForgeryToken]
public IActionResult OpenFile(OrderLineSimpleModel model)
        
            byte[] file = null;

            try
            
                if (model != null)
                
                    //code for getting file from api - part is missing here as not important for this example
                    file = apiHandler.Get<byte[]>(downloadApiUrl, token);

                    var contentDispositionHeader = new System.Net.Mime.ContentDisposition
                    
                        Inline = true,
                        FileName = fileName
                    ;
                    //    Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString() + "; attachment");
                    Response.Headers.Add("Content-Type", "application/pdf");
                    Response.Headers.Add("Content-Disposition", "attachment; filename=" + fileName);
                    Response.Headers.Add("Content-Transfer-Encoding", "binary");
                    Response.Headers.Add("Content-Length", file.Length.ToString());

                
            
            catch (Exception ex)
            
                this.logger.LogError(ex, "Error getting pdf", null);
                return Ok();
            

            return File(file, System.Net.Mime.MediaTypeNames.Application.Pdf);
        

只要您返回响应 200,Ajax 中的成功就可以使用它,您可以检查文件是否实际存在,因为在这种情况下下面的行将是错误的,您可以通知用户:

 if (disposition && disposition.indexOf('attachment') !== -1) 

【讨论】:

【参考方案18】:

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

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

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

$.post("CalculusPage.php", calculusFunction: true, ID: 29, data1: "a", data2: "b" , 功能(数据,状态) 如果(状态==“成功”) /* 2) 在答案中,下载了使用先前计算的页面。例如,这可以是打印在 ajax 调用中计算的表的结果的页面。 */ window.location.href = 下载页面.php+"?ID="+29; ); // 例如:在 CalculusPage.php if ( !empty($_POST["calculusFunction"]) ) $ID = $_POST["ID"]; $query = "INSERT INTO ExamplePage (data1, data2) VALUES ('".$_POST["data1"]."', '".$_POST["data2"]."') WHERE id = ".$ID; ... // 例如:在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"); ...

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

【讨论】:

【参考方案19】:

如果响应是 Array Buffer,请在 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;
      
其中 event.data 是在 xhr 事件的成功函数中收到的响应。

【讨论】:

【参考方案20】:

我需要一个与 @alain-cruz 类似的解决方案,但在 nuxt/vue 中具有多个下载。我知道浏览器会阻止多个文件下载,而且我还有返回一组 csv 格式数据的 API。我一开始打算使用 JSZip,但我需要 IE 支持,所以这是我的解决方案。如果有人可以帮助我改进这一点,那就太好了,但到目前为止它对我有用。

API 返回:

data : 
  body: 
    fileOne: ""col1", "col2", "datarow1.1", "datarow1.2"...so on",
    fileTwo: ""col1", "col2"..."
  

page.vue:

<template>
  <b-link @click.prevent="handleFileExport">Export<b-link>
</template>

export default = 
   data() 
     return 
       fileNames: ['fileOne', 'fileTwo'],
     
   ,
  computed: 
    ...mapState(
       fileOne: (state) => state.exportFile.fileOne,
       fileTwo: (state) => state.exportFile.fileTwo,
    ),
  ,
  method: 
    handleExport() 
      //exportFileAction in store/exportFile needs to return promise
      this.$store.dispatch('exportFile/exportFileAction', paramsToSend)
        .then(async (response) => 
           const downloadPrep = this.fileNames.map(async (fileName) => 
           // using lodash to get computed data by the file name
           const currentData = await _.get(this, `$fileName`);
           const currentFileName = fileName;
           return  currentData, currentFileName ;
         );
         const response = await Promise.all(downloadPrep);
         return response;
       )
       .then(async (data) => 
         data.forEach(( currentData, currentFileName ) => 
           this.forceFileDownload(currentData, currentFileName);
         );
       )
       .catch(console.error);
    ,
    forceFileDownload(data, fileName) 
     const url = window.URL
         .createObjectURL(new Blob([data],  type: 'text/csv;charset=utf-8;' ));
     const link = document.createElement('a');
     link.href = url;
     link.setAttribute('download', `$fileName.csv`);
     document.body.appendChild(link);
     link.click();
   ,

【讨论】:

以上是关于处理从 ajax post 下载的文件的主要内容,如果未能解决你的问题,请参考以下文章

ajax post 数据到php后下载文件!

从 Ajax 下载文件(有点)

ajax请求下载文件无反应

Ajax方式实现文件下载失败

使用 Ajax 从 Servlet 下载文件

ajax方式下载文件