用jQuery下载一个文件。Ajax

我在服务器端有一个Struts2操作用于文件下载。

<action name="download" class="com.xxx.DownAction">
<result name="success" type="stream">
<param name="contentType">text/plain</param>
<param name="inputName">imageStream</param>
<param name="contentDisposition">attachment;filename={fileName}</param>
<param name="bufferSize">1024</param>
</result>
</action>

然而,当我使用jQuery调用动作时:

$.post(
"/download.action",{
para1:value1,
para2:value2
....
},function(data){
console.info(data);
}
);

在Firebug中,我看到数据是用二进制流检索的。我想知道如何打开用户可以在本地保存文件的文件下载窗口 ?

969373 次浏览

没有人张贴这个@Pekka的解决方案…所以我会把它贴出来。它可以帮助别人。

您不需要通过Ajax来实现这一点。只使用

window.location="download.action?para1=value1...."

2019现代浏览器更新

这是我现在推荐的方法,但有几点注意事项:

  • 需要一个相对现代的浏览器
  • 如果文件被期望为非常大的,你可能会做一些类似于最初的方法(iframe和cookie),因为下面的一些操作可能会消耗至少与正在下载的文件一样大的系统内存和/或其他有趣的CPU副作用。

fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// the filename you want
a.download = 'todo-1.json';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
alert('your file has downloaded!'); // or you know, something with better UX...
})
.catch(() => alert('oh no!'));

2012年原创jQuery/iframe/Cookie的方法

蓝色的在这一点上是完全正确的,你不能通过Ajax做到这一点,因为JavaScript不能直接将文件保存到用户的计算机(出于安全考虑)。不幸的是,指向文件下载的主窗口的 URL意味着当文件下载发生时,您几乎无法控制用户体验。

我创建了jQuery文件下载,它允许“类似Ajax”的文件下载体验,完成OnSuccess和OnFailure回调,以提供更好的用户体验。看看我的博客插件解决的常见问题和一些使用它的方法,还有一个jQuery文件下载的演示操作。这里是

下面是一个简单的用例演示,使用带有承诺的插件演示页面还包括许多其他“更好的UX”示例。

$.fileDownload('some/file.pdf')
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });

根据你需要支持的浏览器,你可以使用https://github.com/eligrey/FileSaver.js/,它允许比jQuery文件下载使用的IFRAME方法更显式的控制。

1. 框架不可知:Servlet下载文件作为附件

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
download
</a>


<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Struts2框架:动作下载文件作为附件

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
download
</a>


<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

最好使用<s:a>标记,用OGNL指向用<s:url>标记创建的URL:

<!-- without JS, with Struts tags: THE RIGHT WAY -->
<s:url action="downloadAction.action" var="url">
<s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>

在上述情况下,你需要附加项头写入响应,指定文件需要下载(attachment)而不是由浏览器打开(inline)。你也可以需要来指定内容类型,你可能想要添加文件名和长度(以帮助浏览器绘制一个真实的进度条)。

例如,当下载ZIP文件时:

response.setContentType("application/zip");
response.addHeader("Content-Disposition",
"attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...

使用Struts2(除非你使用Action作为Servlet,例如,一个hack 对于直接流媒体),你不需要直接向响应写入任何东西;简单地使用流结果类型并在struts.xml中配置它就可以了

<result name="success" type="stream">
<param name="contentType">application/zip</param>
<param name="contentDisposition">attachment;filename="${fileName}"</param>
<param name="contentLength">${fileLength}</param>
</result>

3.框架不可知(/ Struts2框架):Servlet(/Action)在浏览器中打开文件

如果你想在浏览器中打开文件,而不是下载它,附加项必须设置为内联,但目标不能是当前窗口位置;你必须将目标指向一个由javascript创建的新窗口,页面中的<iframe>,或者使用"discussed" target="_blank"动态创建的新窗口:

<!-- From a parent page into an IFrame without javascript -->
<a href="downloadServlet?param1=value1" target="iFrameName">
download
</a>


<!-- In a new window without javascript -->
<a href="downloadServlet?param1=value1" target="_blank">
download
</a>


<!-- In a new window with javascript -->
<a href="javascript:window.open('downloadServlet?param1=value1');" >
download
</a>

我创建了一个小函数作为解决方案(灵感来自@JohnCulviner插件):

// creates iframe and form in it with hidden field,
// then submit form with provided data
// url - form url
// data - data to form field
// input_name - form hidden input name


function ajax_download(url, data, input_name) {
var $iframe,
iframe_doc,
iframe_html;


if (($iframe = $('#download_iframe')).length === 0) {
$iframe = $("<iframe id='download_iframe'" +
" style='display: none' src='about:blank'></iframe>"
).appendTo("body");
}


iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
if (iframe_doc.document) {
iframe_doc = iframe_doc.document;
}


iframe_html = "<html><head></head><body><form method='POST' action='" +
url +"'>" +
"<input type=hidden name='" + input_name + "' value='" +
JSON.stringify(data) +"'/></form>" +
"</body></html>";


iframe_doc.open();
iframe_doc.write(iframe_html);
$(iframe_doc).find('form').submit();
}

演示点击事件:

$('#someid').on('click', function() {
ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});

好的,基于ndpu的代码这里有一个改进的(我认为)ajax_download;-

function ajax_download(url, data) {
var $iframe,
iframe_doc,
iframe_html;


if (($iframe = $('#download_iframe')).length === 0) {
$iframe = $("<iframe id='download_iframe'" +
" style='display: none' src='about:blank'></iframe>"
).appendTo("body");
}


iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
if (iframe_doc.document) {
iframe_doc = iframe_doc.document;
}


iframe_html = "<html><head></head><body><form method='POST' action='" +
url +"'>"


Object.keys(data).forEach(function(key){
iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";


});


iframe_html +="</form></body></html>";


iframe_doc.open();
iframe_doc.write(iframe_html);
$(iframe_doc).find('form').submit();
}

像这样使用这个;-

$('#someid').on('click', function() {
ajax_download('/download.action', {'para1': 1, 'para2': 2});
});

参数被作为适当的post params发送,就像来自一个输入,而不是像前面的例子一样作为一个json编码的字符串。

注意:要警惕在这些表单上可能出现的变量注入。可能有一种更安全的方式来编码这些变量。或者考虑逃离它们。

这是使用MVC时的工作代码,你从控制器中获取文件

假设你有你的字节数组声明和填充,你唯一需要做的是使用文件函数(使用System.Web.Mvc)

byte[] bytes = .... insert your bytes in the array
return File(bytes, System.Net.Mime.MediaTypeNames.Application.Octet, "nameoffile.exe");

然后,在同一个控制器中,添加这两个函数

protected override void OnResultExecuting(ResultExecutingContext context)
{
CheckAndHandleFileResult(context);


base.OnResultExecuting(context);
}


private const string FILE_DOWNLOAD_COOKIE_NAME = "fileDownload";


/// <summary>
/// If the current response is a FileResult (an MVC base class for files) then write a
/// cookie to inform jquery.fileDownload that a successful file download has occured
/// </summary>
/// <param name="context"></param>
private void CheckAndHandleFileResult(ResultExecutingContext context)
{
if (context.Result is FileResult)
//jquery.fileDownload uses this cookie to determine that a file download has completed successfully
Response.SetCookie(new HttpCookie(FILE_DOWNLOAD_COOKIE_NAME, "true") { Path = "/" });
else
//ensure that the cookie is removed in case someone did a file download without using jquery.fileDownload
if (Request.Cookies[FILE_DOWNLOAD_COOKIE_NAME] != null)
Response.Cookies[FILE_DOWNLOAD_COOKIE_NAME].Expires = DateTime.Now.AddYears(-1);
}

然后你将能够调用你的控制器来下载并获得"success"或"failure"回调

$.fileDownload(mvcUrl('name of the controller'), {
httpMethod: 'POST',
successCallback: function (url) {
//insert success code


},
failCallback: function (html, url) {
//insert fail code
}
});

你可以用HTML5

注意:返回的文件数据必须是base64编码的,因为JSON不能编码二进制数据

在我的AJAX响应中,我有一个像这样的数据结构:

{
result: 'OK',
download: {
mimetype: string(mimetype in the form 'major/minor'),
filename: string(the name of the file to download),
data: base64(the binary data as base64 to download)
}
}

这意味着我可以执行以下操作来通过AJAX保存文件

var a = document.createElement('a');
if (window.URL && window.Blob && ('download' in a) && window.atob) {
// Do it the HTML5 compliant way
var blob = base64ToBlob(result.download.data, result.download.mimetype);
var url = window.URL.createObjectURL(blob);
a.href = url;
a.download = result.download.filename;
a.click();
window.URL.revokeObjectURL(url);
}

函数base64ToBlob取自在这里,必须遵循此函数使用

function base64ToBlob(base64, mimetype, slicesize) {
if (!window.atob || !window.Uint8Array) {
// The current browser doesn't have the atob function. Cannot continue
return null;
}
mimetype = mimetype || '';
slicesize = slicesize || 512;
var bytechars = atob(base64);
var bytearrays = [];
for (var offset = 0; offset < bytechars.length; offset += slicesize) {
var slice = bytechars.slice(offset, offset + slicesize);
var bytenums = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
bytenums[i] = slice.charCodeAt(i);
}
var bytearray = new Uint8Array(bytenums);
bytearrays[bytearrays.length] = bytearray;
}
return new Blob(bytearrays, {type: mimetype});
};

如果您的服务器正在转储要保存的文件数据,这很好。然而,我还没有完全弄清楚如何实现HTML4的回退

如果你想使用jQuery文件下载,请注意这个IE。 您需要重置响应,否则它将无法下载

    //The IE will only work if you reset response
getServletResponse().reset();
//The jquery.fileDownload needs a cookie be set
getServletResponse().setHeader("Set-Cookie", "fileDownload=true; path=/");
//Do the reset of your action create InputStream and return

您的操作可以实现ServletResponseAware来访问getServletResponse()

在上面添加一些东西来下载文件

下面是一些生成字节数组的java spring代码

@RequestMapping(value = "/downloadReport", method = { RequestMethod.POST })
public ResponseEntity<byte[]> downloadReport(
@RequestBody final SomeObejct obj, HttpServletResponse response) throws Exception {


OutputStream out = new ByteArrayOutputStream();
// write something to output stream
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
respHeaders.add("X-File-Name", name);
ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
return new ResponseEntity<byte[]>(bos.toByteArray(), respHeaders, HttpStatus.CREATED);
}

现在在javascript代码中使用filesver .js,可以下载带有下面代码的文件

var json=angular.toJson("somejsobject");
var url=apiEndPoint+'some url';
var xhr = new XMLHttpRequest();
//headers('X-File-Name')
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 201) {
var res = this.response;
var fileName=this.getResponseHeader('X-File-Name');
var data = new Blob([res]);
saveAs(data, fileName); //this from FileSaver.js
}
}
xhr.open('POST', url);
xhr.setRequestHeader('Authorization','Bearer ' + token);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.responseType = 'arraybuffer';
xhr.send(json);

以上将下载文件

这是我所做的,纯javascript和html。没有测试,但这应该在所有浏览器工作。

Javascript函数

var iframe = document.createElement('iframe');
iframe.id = "IFRAMEID";
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.src = 'SERVERURL'+'?' + $.param($scope.filtro);
iframe.addEventListener("load", function () {
console.log("FILE LOAD DONE.. Download should start now");
});

只使用所有浏览器都支持的组件,没有额外的组件 库。< / p >

enter image description here enter image description here

下面是我的服务器端JAVA Spring控制器代码。

@RequestMapping(value = "/rootto/my/xlsx", method = RequestMethod.GET)
public void downloadExcelFile(@RequestParam(value = "param1", required = false) String param1,
HttpServletRequest request, HttpServletResponse response)
throws ParseException {


Workbook wb = service.getWorkbook(param1);
if (wb != null) {
try {
String fileName = "myfile_" + sdf.format(new Date());
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
wb.write(response.getOutputStream());
response.getOutputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
function downloadURI(uri, name)
{
var link = document.createElement("a");
link.download = name;
link.href = uri;
link.click();
}

我也遇到过同样的问题,并成功地解决了它。我的用例是这样的。

"将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”链接,然后单击它。我在这里做了一个POST请求。相反,您也可以使用简单的GET。我们不能通过Ajax下载文件,必须使用XMLHttpRequest。

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

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

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

让浏览器下载文件的简单方法是像这样发出请求:

 function downloadFile(urlToSend) {
var req = new XMLHttpRequest();
req.open("GET", urlToSend, true);
req.responseType = "blob";
req.onload = function (event) {
var blob = req.response;
var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download=fileName;
link.click();
};


req.send();
}

这将打开浏览器下载弹出。

在Rails中,我是这样做的:

function download_file(file_id) {
let url       = '/files/' + file_id + '/download_file';
$.ajax({
type: 'GET',
url: url,
processData: false,
success: function (data) {
window.location = url;
},
error: function (xhr) {
console.log(' Error:  >>>> ' + JSON.stringify(xhr));
}
});
}

诀窍在于window.location部分。控制器的方法如下所示:

# GET /files/{:id}/download_file/
def download_file
send_file(@file.file,
:disposition => 'attachment',
:url_based_filename => false)
end

我发现了一个修复,虽然它实际上没有使用ajax,它确实允许您使用javascript调用来请求下载,然后在下载实际开始时得到一个回调。我发现,如果链接运行一个服务器端脚本,在发送之前花一点时间来编写文件,那么这很有帮助。所以你可以提醒他们它正在处理,然后当它最终发送文件时,删除处理通知。这就是为什么我想尝试通过ajax加载文件开始,这样我就可以在文件被请求时发生一个事件,而在它实际开始下载时发生另一个事件。

js在首页

function expdone()
{
document.getElementById('exportdiv').style.display='none';
}
function expgo()
{
document.getElementById('exportdiv').style.display='block';
document.getElementById('exportif').src='test2.php?arguments=data';
}

iframe

<div id="exportdiv" style="display:none;">
<img src="loader.gif"><br><h1>Generating Report</h1>
<iframe id="exportif" src="" style="width: 1px;height: 1px; border:0px;"></iframe>
</div>

然后是另一个文件:

<!DOCTYPE html>
<html>
<head>
<script>
function expdone()
{
window.parent.expdone();
}
</script>
</head>
<body>
<iframe id="exportif" src="<?php echo "http://10.192.37.211/npdtracker/exportthismonth.php?arguments=".$_GET["arguments"]; ?>"></iframe>
<script>document.getElementById('exportif').onload= expdone;</script>
</body></html>

我认为有一种方法可以使用js读取数据,这样就不需要php了。但我不知道它的手,我正在使用的服务器支持php,所以这对我有用。我想分享一下,也许能帮到别人。

可以肯定的是,您不能通过Ajax调用来实现这一点。

然而,有一个变通办法。

步骤:

如果你使用form.submit()下载文件,你可以这样做:

  1. 创建一个从客户端到服务器的ajax调用,并将文件流存储在会话中。
  2. 当从服务器返回“成功”时,调用form.submit()来处理存储在会话中的文件流。

当你想要决定是否需要在制作form.submit()后下载文件时,这是有帮助的,例如:在form.submit()上,在服务器端发生异常而不是崩溃的情况下,你可能需要在客户端显示一个自定义消息,在这种情况下,这个实现可能会有帮助。

我为这个问题挣扎了很长一段时间。最后,一个优雅的外部库在这里帮助了我。

使用window.open https://developer.mozilla.org/en-US/docs/Web/API/Window/open

例如,你可以把这行代码放在点击处理程序中:

window.open('/file.txt', '_blank');

它将打开一个新选项卡(因为'_blank' window-name),该选项卡将打开URL。

你的服务器端代码也应该是这样的:

res.set('Content-Disposition', 'attachment; filename=file.txt');

通过这种方式,浏览器应该提示用户将文件保存到磁盘,而不仅仅是向他们显示文件。它还会自动关闭刚刚打开的选项卡。

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


...

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

HTML代码:

<button type="button" id="GetFile">Get File!</button>

jQuery代码:

$('#GetFile').on('click', function () {
$.ajax({
url: 'https://s3-us-west-2.amazonaws.com/s.cdpn.io/172905/test.pdf',
method: 'GET',
xhrFields: {
responseType: 'blob'
},
success: function (data) {
var a = document.createElement('a');
var url = window.URL.createObjectURL(data);
a.href = url;
a.download = 'myfile.pdf';
document.body.append(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
}
});
});

我尝试下载一个CSV文件,然后在下载完成后做一些事情。所以我需要实现一个适当的callback函数。

使用window.location="..."不是一个好主意,因为我不能操作程序后完成下载。像这样,改变头,所以这不是一个好主意。

fetch是一个很好的选择,但是不支持IE 11。而window.URL.createObjectURL不支持IE 11。你可以参考

这是我的密码,它类似于沙赫鲁克·阿拉姆的密码。但是你应该注意window.URL.createObjectURL可能会造成内存泄漏。你可以参考。当响应到达时,数据将被存储到浏览器的内存中。因此,在单击a链接之前,文件已经下载。这意味着下载后你可以做任何事情。

$.ajax({
url: 'your download url',
type: 'GET',
}).done(function (data, textStatus, request) {
// csv => Blob
var blob = new Blob([data]);


// the file name from server.
var fileName = request.getResponseHeader('fileName');


if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else { // for others
var url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);


//Do something after download
...


}
}).then(after_download)
}

如何下载一个文件后收到它的AJAX

当文件创建了很长一段时间,你需要显示PRELOADER时,这很方便

例如,当提交一个web表单:

<script>
$(function () {
$('form').submit(function () {
$('#loader').show();
$.ajax({
url: $(this).attr('action'),
data: $(this).serialize(),
dataType: 'binary',
xhrFields: {
'responseType': 'blob'
},
success: function(data, status, xhr) {
$('#loader').hide();
// if(data.type.indexOf('text/html') != -1){//If instead of a file you get an error page
//     var reader = new FileReader();
//     reader.readAsText(data);
//     reader.onload = function() {alert(reader.result);};
//     return;
// }
var link = document.createElement('a'),
filename = 'file.xlsx';
// if(xhr.getResponseHeader('Content-Disposition')){//filename
//     filename = xhr.getResponseHeader('Content-Disposition');
//     filename=filename.match(/filename="(.*?)"/)[1];
//     filename=decodeURIComponent(escape(filename));
// }
link.href = URL.createObjectURL(data);
link.download = filename;
link.click();
}
});
return false;
});
});
</script>

可选函数被注释掉以简化示例。

不需要在服务器上创建临时文件。

jQuery v2.2.4 OK。旧版本将会有一个错误:

Uncaught DOMException: Failed to read the 'responseText' property from 'XMLHttpRequest': The value is only accessible if the object's 'responseType' is '' or 'text' (was 'blob').

这就是它在任何浏览器中都很好(我使用asp.net核心)

            function onDownload() {


const api = '@Url.Action("myaction", "mycontroller")';
var form = new FormData(document.getElementById('form1'));


fetch(api, { body: form, method: "POST"})
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
$('#linkdownload').attr('download', 'Attachement.zip');
$('#linkdownload').attr("href", url);
$('#linkdownload')
.fadeIn(3000,
function() { });


})
.catch(() => alert('An error occurred'));






}

<button type="button" onclick="onDownload()" class="btn btn-primary btn-sm">Click to Process Files</button>
 

 

 

<a role="button" href="#" style="display: none" class="btn btn-sm btn-secondary" id="linkdownload">Click to download Attachments</a>
 

 

<form asp-controller="mycontroller" asp-action="myaction" id="form1"></form>
 

        function onDownload() {
const api = '@Url.Action("myaction", "mycontroller")';
//form1 is your id form, and to get data content of form
var form = new FormData(document.getElementById('form1'));


fetch(api, { body: form, method: "POST"})
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
$('#linkdownload').attr('download', 'Attachments.zip');
$('#linkdownload').attr("href", url);
$('#linkdownload')
.fadeIn(3000,
function() {


});
})
.catch(() => alert('An error occurred'));


}

如果服务器在响应中写入文件(包括cookie) 你使用它们来确定文件下载是否开始),简单地创建一个带有值的表单并提交它:

function ajaxPostDownload(url, data) {
var $form;
if (($form = $('#download_form')).length === 0) {
$form = $("<form id='download_form'" + " style='display: none; width: 1px; height: 1px; position: absolute; top: -10000px' method='POST' action='" + url + "'></form>");
$form.appendTo("body");
}
//Clear the form fields
$form.html("");
//Create new form fields
Object.keys(data).forEach(function (key) {
$form.append("<input type='hidden' name='" + key + "' value='" + data[key] + "'>");
});
//Submit the form post
$form.submit();
}

用法:

ajaxPostDownload('/fileController/ExportFile', {
DownloadToken: 'newDownloadToken',
Name: $txtName.val(),
Type: $txtType.val()
});

控制器方法:

[HttpPost]
public FileResult ExportFile(string DownloadToken, string Name, string Type)
{
//Set DownloadToken Cookie.
Response.SetCookie(new HttpCookie("downloadToken", DownloadToken)
{
Expires = DateTime.UtcNow.AddDays(1),
Secure = false
});


using (var output = new MemoryStream())
{
//get File
return File(output.ToArray(), "application/vnd.ms-excel", "NewFile.xls");
}
}

我的方法完全基于jQuery。对我来说,问题是它必须是一个POST-HTTP调用。我希望它是由jQuery单独完成。

解决方案:

$.ajax({
type: "POST",
url: "/some/webpage",
headers: {'X-CSRF-TOKEN': csrfToken},
data: additionalDataToSend,
dataType: "text",
success: function(result) {
let blob = new Blob([result], { type: "application/octetstream" });


let a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = "test.xml";;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(a.href);
                        

...
},
error: errorDialog
});

解释:

我和很多人所做的是在网页上创建一个链接,指出应该下载目标,并将http-request的结果作为目标。在此之后,我将链接附加到文档,而不是简单地点击链接,然后删除链接。你不再需要iframe了。

神奇之处在于线条

let blob = new Blob([result], { type: "application/octetstream" });
a.href = window.URL.createObjectURL(blob);
有趣的是,这个解决方案只适用于""。正如您可以在其他回答中看到的,一些只是使用一个blob,但没有解释为什么和如何创建它。 正如你可以读到的,例如在Mozilla开发人员文档中,你需要一个文件,媒体资源或blob来实现函数"去工作。问题是您的http-response可能不是其中任何一个。 因此,您必须做的第一件事是将响应转换为blob。这是第一行的作用。然后你可以使用&;__abc0 &;用你新创建的blob。 如果你点击链接,你的浏览器会打开一个文件保存对话框,你可以保存你的数据。显然,您可能无法为要下载的文件定义固定的文件名。然后你必须让你的回答更复杂,就像Luke的回答一样

不要忘记释放内存,特别是在处理大文件时。有关更多示例和信息,您可以查看JS blob对象的详细信息

我尝试了Ajax和HttpRequest方法来获得我的结果下载文件,但我失败了,最后我用这些步骤解决了我的问题:

在我的HTML代码中实现了一个简单的隐藏表单:

<form method="post" id="post_form" style="display:none" action="amin.php" >
<input type="hidden" name="action" value="export_xlsx" />
<input type="hidden" name="post_form_data" value="" />
</form>

输入与'动作'名称是调用函数在我的PHP代码, 输入'post_form_data'名称,用于发送无法使用GET发送的表的长数据。此数据被编码为json,并将json放在input:

var list = new Array();
$('#table_name tr').each(function() {
var row = new Array();
$(this).find('td').each(function() {
row.push($(this).text());
});
list.push(row);
});
list    = JSON.stringify(list);


$("input[name=post_form_data]").val(list);

现在,表单已经准备好了,输入了我想要的值,只需要触发提交。

document.getElementById('post_form').submit();
< p >和完成了! 而我的结果是一个文件(xlsx文件为我)的页面不会被重定向和即时文件开始下载在最后一页,所以不需要使用iframe或窗口。打开等。< / p >

如果你正在尝试这样做,这应该是一个简单的技巧😉。