一、背景
有一个下载文件的需求,最初前端是使用form做的,后端返回一个文件流,大概是下面这个样子
<form method="post" asp-controller="DownloadTest" id="inputForm" class="xlmButton">
<div class="flex" style="width:450px;justify-content:end;margin-top:20px">
<div class="download-box flex-align-center flex common-btn btn-warning relative">
<input class='btn-outline-secondary' type="submit" id="downloadfile1" value=("Download") asp-action="GetDisclosureFile" class="glossyBtn" />
</div>
</div>
</form>
Asp.net 代码
[HttpPost]
[ResponseCache(NoStore = true)]
public async Task<IActionResult> Download([FromServices] IHostingEnvironment hostingEnvironment)
{
var datafromblob = await file.OpenReadAsync();
Stream blobContent = datafromblob;
// Download the file details async
var content = await file.DownloadContentAsync();
return File(blobContent, "application/octet-stream", filename + ".xlsx");
}
上面的代码,在后端返回文件流的时候,form会自己下载文件。
现在需求发生了改变,需要在下载文件的时候增加Loading遮罩,查阅文档发现,form自身的提交事件无法处理返回值,因为没有请求成功的回调函数。
二、使用ajax发送请求和处理响应
在这里我使用的是<script src="https://malsup.github.io/jquery.form.js"></script>
form插件,用来接收响应,本质上和ajax是一样的。
$(function () {
var options = {
type: 'POST',
url: '@Url.Action("Download", "DownloadTest")',
success:function(responseText, statusText, xhr){
let url = window.URL.createObjectURL(new Blob([responseText],{type: 'application/octet-stream;charset=Unicode'}))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', 'text.xlsx')
document.body.appendChild(link)
link.click();
link.remove();
$('.show-loading').addClass('none')
},
dataType: 'text',
error : function(xhr, status, err) {
debugger;
console.log('error')
console.log(err)
$('.show-loading').addClass('none')
}
};
$("#inputForm").submit(function(){
debugger;
$(this).ajaxSubmit(options);
return false; //防止表单自动提交
// return true; //表单自动提交
});
});
这样可以下载,但是文件打开会提示损坏了。。。
我们可以看到dataType是text,虽然后端返回的是数据流,按理来说,返回到前端应该是二进制数据,通过typeof看到,其实是Unicode编码的字符串,导致new 出来的Blob不正确,下载的文件也无法打开。如果修改了dataType的值,比如修改成blob,会报错,提示text不能转换成blob。
三、问题解决
使用XMLHttpRequest下载文件
$(function () {
$("#inputForm").submit(function(){
// $(this).ajaxSubmit(options);
$('.show-loading').removeClass('none')
var filename = "test";
const formdata = new FormData()
formdata.append('val1', $('#val1').val());
formdata.append('val2',$('#val2').val());
var xhr = new XMLHttpRequest();
xhr.open("Post", '@Url.Action("Download", "DownloadTest")', true);
xhr.responseType = "blob";
xhr.onreadystatechange = function (e) {
console.log('onreadystatechange')
if (xhr.readyState==4 && xhr.status==200) {
} };
xhr.onload = function (e) {
console.log('onload')
// 请求完成
if (this.status === 200) { // 返回200
var blob = this.response;
var reader = new FileReader();
reader.readAsDataURL(blob); // 转换为base64,可以直接放入a表情href
reader.onload = function (e) {
// 转换完成,创建一个a标签用于下载
var a = document.createElement('a');
var nameFile = filename + `.xlsx`
a.download = nameFile;
a.href = e.target.result;
$("body").append(a); // 修复firefox中无法触发click
a.click();
$(a).remove();
}
}
$('.show-loading').addClass('none')
};
xhr.onerror = function (e) {
// alert(e+" error");
console.log(e)
$('.show-loading').addClass('none')
};
xhr.send(formdata);
return false; //防止表单自动提交
});
});
问题解决。