处理从ajax发布的文件下载


392

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

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


:对于那些谁看过这篇文章,阅读这篇文章stackoverflow.com/questions/20830309/...
苏班

我已经从问题中删除了您的解决方案。欢迎您在下面将其发布为答案,但它不属于问题。
马丁·彼得斯

Answers:


111

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

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


35
如问题中所述:“也不能使用纯HTML表单。”
Pavle Predic

13
否,因为使用常规POST会将浏览器导航到POST URL。我不想离开页面。我想在后台执行请求,处理响应并将其呈现给客户端。
Pavle Predic

6
如果服务器像其他答案一样发送回标头,则会在新窗口中打开-我之前已经完成过。仅当您的服务器端脚本返回HTML代码时,它才会导航

1
@PavlePredic您是否最终弄清楚了如何管理两种响应方案,即JSON文本响应或下载文件响应?
网络用户

9
答案不明确,建议的解决方案不起作用。
stack247'9

530

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

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
    if (this.status === 200) {
        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 type = xhr.getResponseHeader('Content-Type');

        var blob;
        if (typeof File === 'function') {
            try {
                blob = new File([this.response], filename, { type: type });
            } catch (e) { /* Edge */ }
        }
        if (typeof blob === 'undefined') {
            blob = new Blob([this.response], { type: type });
        }

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

这是使用jQuery.ajax的旧版本。当响应转换为某些字符集的字符串时,它可能会破坏二进制数据。

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, 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, '');
        }

        var type = xhr.getResponseHeader('Content-Type');
        var blob = new Blob([response], { type: type });

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

1
非常感谢 !我必须在HTTP响应中将“ Content-disposition”添加到“ Access-Control-Expose-Headers”和“ Access-Control-Allow-Headers”中,以使其正常运行。
JulienD

1
文件大于500 mb时不起作用,也许我们应该使用其他api?
希尔拉

如何从清理部分(而不只是URL)中的DOM中删除a元素?document.body.removeChild(a);
Scoregraphic '19

@hirra使用responceType“ blob”代替“ arraybuffer”并以这种方式重写onload回调函数,即var blob是this.response(var blob = this.response;)所以var blob =this.responce; /** if (typeof File === 'function') { try { blob = new File([this.response], filename, { type: type }); } catch (e) { /* Edge */ } } if (typeof blob === 'undefined') { blob = new Blob([this.response], { type: type }); } */
Chris Tobba

1
这是完美的解决方案。只是一个小小的改变。在打字稿中,我需要window.location.href = downloadUrl代替window.location = downloadUrl
michal.jakubeczy

39

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

“将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));
});

上面的片段只是在做以下

  • 使用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调整为以上一种。您需要根据文件类型进行设置。您可以使用此技术下载任何类型的文件。


33

您正在使用哪种服务器端语言?在我的应用程序中,通过在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的下载链接而不是直接强制下载,在当前设置中,您将必须同时更新客户端和服务器端。


24

对于那些从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属性记录上载时的文件名。有关更多详细信息,请参见文件输入中的MDN文档
Tim Hettler


22

这是我如何工作的 https://stackoverflow.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>才能正常工作吗?
gbade_

我也收到了您提供的两个示例的空白pdf文件。我正在尝试让它下载pdf文件。
gbade_

@gbade_不,您不需要任何特定的jQuery版本。应该适用于所有版本。您是否检查要下载的PDF是否具有正确的CORS标头?控制台上的任何错误都可能有助于调试
Mayur Padshala,

这对我来说使用download.js起作用: success: function (response, status, request) { download(response, "filename.txt", "application/text"); }
有用

12

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

几周前我遇到了同样的问题,实际上不可能通过AJAX实现“干净”下载,Filament Group创建了一个jQuery插件,该插件的工作原理与您已经发现的完全一样,即jQuery File下载但是此技术有一个缺点。

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

如果您使用的是小于1MB的小文件,则不会注意到这一点。但是,正如我在自己的应用程序中发现的那样,对于更大的文件大小,这几乎是难以忍受的。

我的应用程序允许用户导出动态生成的图像,这些图像通过POST请求以base64格式发送到服务器(这是唯一可行的方式),然后进行处理并以.png,.jpg文件,base64的形式发送回用户+ 1MB的图像字符串很大,这迫使用户要花更多的时间等待文件开始下载。在慢速的Internet连接中,这确实很烦人。

为此,我的解决方案是将文件临时写入服务器,一旦准备好,就以按钮的形式动态生成指向文件的链接,该按钮在“请稍候...”和“下载”状态之间切换,并且保持不变然后,在预览弹出窗口中打印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");
卡斯珀(Casper)

5

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

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

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

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

  4. 您只能使用表单支持的内容类型。这意味着您不能使用JSON。

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


5

对于寻求更现代方法的用户,可以使用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。您可以在以下[link] [1]中找到完整的浏览器兼容性列表。

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


1
太棒了!要在新标签页而不是下载弹出窗口中打开它,请使用:``const window = open(downloadUrl,“ _blank”); if(window!== null)window.focus(); ```
andy

我有办法对多组数据执行此操作吗?例如,从api调用中获取{fileOne:data,fileTwo:data,fileThree:data}并一次生成三个下载文件?谢谢!
il0v3d0g

嗯,我没有尝试过。但是您始终可以将图像压缩为zip文件并下载。我将检查是否可能。
阿兰·克鲁兹

4

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

//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进行相同的操作。


3

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

一个用于执行此操作的非常简单的库是jquery.redirect。它提供类似于标准jQuery.post方法的API :

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

3

这是一个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,这可能会更容易,但这不是我的情况。


1
这不是ajax帖子,因为问题是使用ajax
Nidhin David

这只是解决上述问题的方法,但不是针对ajax调用的。
诺米·阿里

1

我使用了FileSaver.js。就csv文件而言,我这样做是(用文字):

  $.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的回答相同的方法。


1
..但是您通常可以在iOS中下载文件吗?
亚历克斯·马歇尔


1

为了获得Jonathan Amends 在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 });

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


0

这是我的解决方案,来自不同的来源:服务器端实现:

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

0

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

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

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

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

       功能(数据,状态) 
       {
            如果(状态==“成功”) 
            {
                / * 2)在答案中,下载了使用先前计算的页面。例如,这可以是打印ajax调用中计算出的表的结果的页面。* /
                window.location.href = DownloadPage.php +“?ID =” + 29;
            }               
       }
);

//例如:在CalculusPage.php中

    如果(!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”);

    ...

我希望这个解决方案对我来说对许​​多人都有用。


0

如果response是一个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事件的成功函数中收到的响应。

0

以下是我的解决方案,该解决方案用于根据包含某些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中的成功就可以使用它,您可以检查文件是否实际存在,因为在这种情况下,以下行将为false,您可以通知用户:

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

0

我需要与@ alain-cruz相似的解决方案,但在nuxt / vue中需要多次下载。我知道浏览器会阻止多个文件下载,我也有API,该API返回一组csv格式的数据。起初我打算使用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();
   },
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.