Excel 通过 ajax 方法调用函数时未下载文件
Excel file not being downloaded when function is called via ajax method
情况
我正在开发一个应用程序,我可以在其中创建一个包含 X 个项目的网格,并且每个项目都有一个打印按钮。单击此打印按钮允许我调用 ajax 函数,该函数将网格项的 ID 传递给控制器。我根据该 ID 检索相关数据,然后将其下载到 excel 文件中。 (具体项目的检索尚未完成)
我目前有什么
到目前为止,我已经有了下载 excel 文件以及我的网格的基本代码。
问题
我面临的问题是,如果我单击 "Print" 按钮...没有任何反应,即使我的 exporttoexcel
函数中有一个断点也显示我已输入该函数并且我可以逐步通过它,尽管没有错误,但什么也没有发生。但是,我添加了调用相同函数的随机按钮,当我单击该按钮时,下载了 excel 文件。因此,我认为这个问题与 aJax.
有关
代码
<input type="button" value="Test" onclick="location.href='@Url.Action("ExportToExcel", "Profile")'" />
这是下载文件的代码。这是我添加的一个简单按钮。
function ExportToExcel(id) {
$.ajax({
type: "POST",
url: "@Url.Action("ExportToExcel", "Profile")",
data: { "id": id },
dataType: "json"
});
}
这是我想要工作的功能,但它不工作,我看不出哪里错了。
导出到 Excel 代码
public void ExportToExcelx()
{
var products = new System.Data.DataTable("teste");
products.Columns.Add("col1", typeof(int));
products.Columns.Add("col2", typeof(string));
products.Rows.Add(1, "product 1");
products.Rows.Add(2, "product 2");
products.Rows.Add(3, "product 3");
products.Rows.Add(4, "product 4");
products.Rows.Add(5, "product 5");
products.Rows.Add(6, "product 6");
products.Rows.Add(7, "product 7");
var grid = new GridView();
grid.DataSource = products;
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
Response.ContentType = "application/ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
//Response.Output.Write(sw.ToString());
//Response.Flush();
//Response.End();
// =============
//Open a memory stream that you can use to write back to the response
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
StreamReader sr = new StreamReader(s, Encoding.ASCII);
//Write the stream back to the response
Response.Write(sr.ReadToEnd());
Response.End();
// return View("MyView");
}
理论
我相信这个错误与 aJax 有某种关联,我也在这样创建控制器中的按钮。
"<button type='button' class='btn btn-warning' onclick='ExportToExcel(" + c.id + ");'>Print</button>",
由于 location.href='@Url.Action
有效,我想知道尝试重做我的动态按钮是否可以解决我的问题。
感谢您提供的任何见解。
是的,你是对的,你在使用 ajax 时遇到了问题,基本上当你第一次 ajax 调用 return 时,你必须再次调用控制器操作 ajax成功。将以下代码片段添加到您的 ajax 调用中。
success: function () {
window.location = '@Url.Action("ExportExcel", "Profile")?id='+id;
}
并且您必须将控制器方法更改为 return 文件,如下所示
public FileResult ExportToExcelx()
{
...............
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
return File(byteArray, System.Net.Mime.MediaTypeNames.Application.Octet, "FileName.xlsx");
}
这个问题有多种解决方案:
Solution 1
假设您有一个这样的产品模型:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
在你的控制器中:
[HttpPost]
public JsonResult ReportExcel(string id)
{
// Your Logic Here: <DB operation> on input id
// or whatsoever ...
List<Product> list = new List<Product>() {
new Product{ Id = 1, Name = "A"},
new Product{ Id = 2, Name = "B"},
new Product{ Id = 3, Name = "C"},
};
return Json(new { records = list }, JsonRequestBehavior.AllowGet);
}
然后在您的视图 (.cshtml) 中,使用 JSONToCSVConvertor
作为实用函数,只是不要触摸它,因为它会转换 [=38= json 个对象 的数组接收到 Excel 并提示下载。
@{
ViewBag.Title = "View export to Excel";
}
<h2>....</h2>
@* All Your View Content goes here *@
@* This is a sample form *@
<form>
<div class="form-group">
<label>Product ID</label>
<div class="col-md-10">
<input id="productID" name="productID" class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="submit" type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</form>
@section scripts{
<script>
$('#submit').click(function (e) {
e.preventDefault();
var ID = $('#productID').val();
$.ajax({
cache: false,
type: 'POST',
url: '/YourControllerName/ReportExcel',
data: {id: ID},
success: function (data) {
console.log(data);
JSONToCSVConvertor(data.records, "Sample Report", true);
}
})
});
function JSONToCSVConvertor(JSONData, ReportTitle, ShowLabel) {
//If JSONData is not an object then JSON.parse will parse the JSON string in an Object
var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;
var CSV = 'sep=,' + '\r\n\n';
//This condition will generate the Label/Header
if (ShowLabel) {
var row = "";
//This loop will extract the label from 1st index of on array
for (var index in arrData[0]) {
//Now convert each value to string and comma-seprated
row += index + ',';
}
row = row.slice(0, -1);
//append Label row with line break
CSV += row + '\r\n';
}
//1st loop is to extract each row
for (var i = 0; i < arrData.length; i++) {
var row = "";
//2nd loop will extract each column and convert it in string comma-seprated
for (var index in arrData[i]) {
row += '"' + arrData[i][index] + '",';
}
row.slice(0, row.length - 1);
//add a line break after each row
CSV += row + '\r\n';
}
if (CSV == '') {
alert("Invalid data");
return;
}
//Generate a file name
var fileName = "MyReport_";
//this will remove the blank-spaces from the title and replace it with an underscore
fileName += ReportTitle.replace(/ /g, "_");
//Initialize file format you want csv or xls
var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);
// Now the little tricky part.
// you can use either>> window.open(uri);
// but this will not work in some browsers
// or you will not get the correct file extension
//this trick will generate a temp <a /> tag
var link = document.createElement("a");
link.href = uri;
//set the visibility hidden so it will not effect on your web-layout
link.style = "visibility:hidden";
link.download = fileName + ".csv";
//this part will append the anchor tag and remove it after automatic click
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
</script>
}
我 运行 并成功构建了上面的代码,所以请随意抓取并根据需要进行调整。
这里还有 jsFiddle link,感谢它的开发者:https://jsfiddle.net/1ecj1rtz/
Solution 2
通过$.ajax
调用此操作方法并下载文件:
public FileResult Export(int id)
{
//......... create the physical file ....//
byte[] fileBytes = File.ReadAllBytes(filePath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
连同解决方案 2,此线程为您提供了一个好主意:
希望这对您有所帮助。 :)
答案:
You need to include success in your ajax call.
function ExportToExcel(id) {
$.ajax({
type: "POST",
url: "@Url.Action("ExportToExcel", "Profile")",
data: { "id": id },
dataType: "json"
success: function () {
window.location = '@Url.Action("ExportExcel", "Profile")?id='+id;
}
});
}
For Duplicate headers received from the server
Duplicate headers received from server
数据将在 AJAX GET 请求中转换为查询字符串;只需使用 jQuery 参数函数自己做:
$('#excel').on('click',function(){
var query = {
location: $('#location').val(),
area: $('#area').val(),
booth: $('#booth').val()
}
var url = "{{URL::to('downloadExcel_location_details')}}?" + $.param(query)
window.location = url;
});
在触发完全 post 返回之前无法下载文件。以下是您的操作方法:
您的 ExportToExcelx 函数将在 TempData 对象中保存文件,如下所示:
TempData["fileHandle"] = s.ToArray();
而不是return正在查看return临时数据标识符"fileHandle"和文件名,如下所示:
return Json(new { fileHandle = "fileHandle", FileName = "file.xls" }, JsonRequestBehavior.AllowGet);
所以你修改后的函数是这样的:
public JsonResult ExportToExcelx()
{
var products = new System.Data.DataTable("teste");
products.Columns.Add("col1", typeof(int));
products.Columns.Add("col2", typeof(string));
products.Rows.Add(1, "product 1");
products.Rows.Add(2, "product 2");
products.Rows.Add(3, "product 3");
products.Rows.Add(4, "product 4");
products.Rows.Add(5, "product 5");
products.Rows.Add(6, "product 6");
products.Rows.Add(7, "product 7");
var grid = new GridView();
grid.DataSource = products;
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
Response.ContentType = "application/ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
//Response.Output.Write(sw.ToString());
//Response.Flush();
//Response.End();
// =============
//Open a memory stream that you can use to write back to the response
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
TempData["fileHandle"] = s.ToArray();
//StreamReader sr = new StreamReader(s, Encoding.ASCII);
//Write the stream back to the response
Response.Write(sr.ReadToEnd());
Response.End();
return Json(new { fileHandle = "fileHandle", FileName = "file.xls" }, JsonRequestBehavior.AllowGet);
// return View("MyView");
}
现在您需要控制器中的另一个功能来下载如下文件:
[HttpGet]
public virtual ActionResult Download(string fileHandle, string fileName)
{
if (TempData[fileHandle] != null)
{
byte[] data = TempData[fileHandle] as byte[];
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
return new EmptyResult();
}
}
成功调用 ExportToExcelx 函数后,您的 ajax 调用将调用下载函数,如下所示:
$.ajax({
type: 'GET',
cache: false,
url: '/url',
success: function (data) {
window.location = '/url/Download?fileHandle=' + data.fileHandle
+ '&filename=' + data.FileName; //call download function
},
error: function (e) {
//handle error
}
下载函数然后将 return 文件。
希望对您有所帮助。
我在这里遇到过类似的问题,它也通过动态按钮解决了。我只需要在我的请求中包含一个 responseType:'blob'
。
并获得对按钮的响应:
var link = document.createElement('a');
link.href = window.URL.createObjectURL(response.data);
link.download='filename.xlsx';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
我的控制器写入输出流并生成 "application/xls"
response.setContentType("application/xls");
response.setHeader("Content-disposition", "attachment;");
response.getOutputStream().write(content);
首先,我不会用GridView生成excel。尽管是 "easy",它不会生成实际的 excel 文件,而是生成一个带有 xls 扩展名的 html 文件:
<div>
<table cellspacing="0" rules="all" border="1" style="border-collapse:collapse;">
<tr>
<th scope="col">col1</th><th scope="col">col2</th>
</tr><tr>
<td>1</td><td>product 1</td>
</tr><tr>
<td>2</td><td>product 2</td>
</tr><tr>
<td>3</td><td>product 3</td>
</tr><tr>
<td>4</td><td>product 4</td>
</tr><tr>
<td>5</td><td>product 5</td>
</tr><tr>
<td>6</td><td>product 6</td>
</tr><tr>
<td>7</td><td>product 7</td>
</tr>
</table>
</div>
这很烦人(而且不专业)。
如果您不受限于旧 excel 版本 - xls - 但可以使用最新的文件格式 xlsx,我宁愿使用 DocumentFormat.OpenXml nuget 包或其他 packages/libraries for excel 一代。
老实说,DocumentFormat.OpenXml 功能强大但使用起来有点乏味,当您有很多列并且只有 objects 的平面列表要报告时。
如果您使用的是 .NET Framework(不是 Dotnet Core),您可以尝试
CsvHelper.Excel nuget 包。用法非常简单。您的 ExportToExcel 方法将变为:
public ActionResult ExportToExcel(string id)
{
// TODO: Replace with correct products retrieving logic using id input
var products = new [] {
{ col1 = 1, col2 = "product 1" },
{ col1 = 2, col2 = "product 2" },
{ col1 = 3, col2 = "product 3" },
{ col1 = 4, col2 = "product 4" },
{ col1 = 5, col2 = "product 5" },
{ col1 = 6, col2 = "product 6" },
{ col1 = 7, col2 = "product 7" },
{ col1 = 1, col2 = "product 1" },
{ col1 = 1, col2 = "product 1" },
};
var ms = new MemoryStream();
var workbook = new XLWorkbook();
using (var writer = new CsvWriter(new ExcelSerializer(workbook)))
{
writer.WriteRecords(products);
}
workbook.SaveAs(ms);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
return File(ms, MimeMapping.GetMimeMapping("file.xlsx"), $"MyExcelFile.xlsx");
}
另一个非常强大的包是 EPPlus,它允许您加载数据表(参见:https://whosebug.com/a/53957999/582792)。
来到 AJAX 部分,嗯...我认为您根本不需要它:一旦您将位置设置为新的 ExportToExcel 操作,它应该只下载文件。
假设您使用 Bootstrap 3,对于 collection 中的每个项目,您可以:
<a href="@Url.Action("ExportToExcel", "Profile", new {id=item.Id})" class="btn btn-info">
<i class="glyphicon glyphicon-download-alt" />
</a>
以下是我如何使它适用于 PDF。 Excel 下载应该差不多
$.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();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
或使用 download.js
$.ajax({
url: '<URL_TO_FILE>',
success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});
情况
我正在开发一个应用程序,我可以在其中创建一个包含 X 个项目的网格,并且每个项目都有一个打印按钮。单击此打印按钮允许我调用 ajax 函数,该函数将网格项的 ID 传递给控制器。我根据该 ID 检索相关数据,然后将其下载到 excel 文件中。 (具体项目的检索尚未完成)
我目前有什么
到目前为止,我已经有了下载 excel 文件以及我的网格的基本代码。
问题
我面临的问题是,如果我单击 "Print" 按钮...没有任何反应,即使我的 exporttoexcel
函数中有一个断点也显示我已输入该函数并且我可以逐步通过它,尽管没有错误,但什么也没有发生。但是,我添加了调用相同函数的随机按钮,当我单击该按钮时,下载了 excel 文件。因此,我认为这个问题与 aJax.
代码
<input type="button" value="Test" onclick="location.href='@Url.Action("ExportToExcel", "Profile")'" />
这是下载文件的代码。这是我添加的一个简单按钮。
function ExportToExcel(id) {
$.ajax({
type: "POST",
url: "@Url.Action("ExportToExcel", "Profile")",
data: { "id": id },
dataType: "json"
});
}
这是我想要工作的功能,但它不工作,我看不出哪里错了。
导出到 Excel 代码
public void ExportToExcelx()
{
var products = new System.Data.DataTable("teste");
products.Columns.Add("col1", typeof(int));
products.Columns.Add("col2", typeof(string));
products.Rows.Add(1, "product 1");
products.Rows.Add(2, "product 2");
products.Rows.Add(3, "product 3");
products.Rows.Add(4, "product 4");
products.Rows.Add(5, "product 5");
products.Rows.Add(6, "product 6");
products.Rows.Add(7, "product 7");
var grid = new GridView();
grid.DataSource = products;
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
Response.ContentType = "application/ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
//Response.Output.Write(sw.ToString());
//Response.Flush();
//Response.End();
// =============
//Open a memory stream that you can use to write back to the response
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
StreamReader sr = new StreamReader(s, Encoding.ASCII);
//Write the stream back to the response
Response.Write(sr.ReadToEnd());
Response.End();
// return View("MyView");
}
理论
我相信这个错误与 aJax 有某种关联,我也在这样创建控制器中的按钮。
"<button type='button' class='btn btn-warning' onclick='ExportToExcel(" + c.id + ");'>Print</button>",
由于 location.href='@Url.Action
有效,我想知道尝试重做我的动态按钮是否可以解决我的问题。
感谢您提供的任何见解。
是的,你是对的,你在使用 ajax 时遇到了问题,基本上当你第一次 ajax 调用 return 时,你必须再次调用控制器操作 ajax成功。将以下代码片段添加到您的 ajax 调用中。
success: function () {
window.location = '@Url.Action("ExportExcel", "Profile")?id='+id;
}
并且您必须将控制器方法更改为 return 文件,如下所示
public FileResult ExportToExcelx()
{
...............
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
return File(byteArray, System.Net.Mime.MediaTypeNames.Application.Octet, "FileName.xlsx");
}
这个问题有多种解决方案:
Solution 1
假设您有一个这样的产品模型:
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
在你的控制器中:
[HttpPost]
public JsonResult ReportExcel(string id)
{
// Your Logic Here: <DB operation> on input id
// or whatsoever ...
List<Product> list = new List<Product>() {
new Product{ Id = 1, Name = "A"},
new Product{ Id = 2, Name = "B"},
new Product{ Id = 3, Name = "C"},
};
return Json(new { records = list }, JsonRequestBehavior.AllowGet);
}
然后在您的视图 (.cshtml) 中,使用 JSONToCSVConvertor
作为实用函数,只是不要触摸它,因为它会转换 [=38= json 个对象 的数组接收到 Excel 并提示下载。
@{
ViewBag.Title = "View export to Excel";
}
<h2>....</h2>
@* All Your View Content goes here *@
@* This is a sample form *@
<form>
<div class="form-group">
<label>Product ID</label>
<div class="col-md-10">
<input id="productID" name="productID" class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input id="submit" type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</form>
@section scripts{
<script>
$('#submit').click(function (e) {
e.preventDefault();
var ID = $('#productID').val();
$.ajax({
cache: false,
type: 'POST',
url: '/YourControllerName/ReportExcel',
data: {id: ID},
success: function (data) {
console.log(data);
JSONToCSVConvertor(data.records, "Sample Report", true);
}
})
});
function JSONToCSVConvertor(JSONData, ReportTitle, ShowLabel) {
//If JSONData is not an object then JSON.parse will parse the JSON string in an Object
var arrData = typeof JSONData != 'object' ? JSON.parse(JSONData) : JSONData;
var CSV = 'sep=,' + '\r\n\n';
//This condition will generate the Label/Header
if (ShowLabel) {
var row = "";
//This loop will extract the label from 1st index of on array
for (var index in arrData[0]) {
//Now convert each value to string and comma-seprated
row += index + ',';
}
row = row.slice(0, -1);
//append Label row with line break
CSV += row + '\r\n';
}
//1st loop is to extract each row
for (var i = 0; i < arrData.length; i++) {
var row = "";
//2nd loop will extract each column and convert it in string comma-seprated
for (var index in arrData[i]) {
row += '"' + arrData[i][index] + '",';
}
row.slice(0, row.length - 1);
//add a line break after each row
CSV += row + '\r\n';
}
if (CSV == '') {
alert("Invalid data");
return;
}
//Generate a file name
var fileName = "MyReport_";
//this will remove the blank-spaces from the title and replace it with an underscore
fileName += ReportTitle.replace(/ /g, "_");
//Initialize file format you want csv or xls
var uri = 'data:text/csv;charset=utf-8,' + escape(CSV);
// Now the little tricky part.
// you can use either>> window.open(uri);
// but this will not work in some browsers
// or you will not get the correct file extension
//this trick will generate a temp <a /> tag
var link = document.createElement("a");
link.href = uri;
//set the visibility hidden so it will not effect on your web-layout
link.style = "visibility:hidden";
link.download = fileName + ".csv";
//this part will append the anchor tag and remove it after automatic click
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
</script>
}
我 运行 并成功构建了上面的代码,所以请随意抓取并根据需要进行调整。
这里还有 jsFiddle link,感谢它的开发者:https://jsfiddle.net/1ecj1rtz/
Solution 2
通过$.ajax
调用此操作方法并下载文件:
public FileResult Export(int id)
{
//......... create the physical file ....//
byte[] fileBytes = File.ReadAllBytes(filePath);
return File(fileBytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
}
连同解决方案 2,此线程为您提供了一个好主意:
希望这对您有所帮助。 :)
答案:
You need to include success in your ajax call.
function ExportToExcel(id) {
$.ajax({
type: "POST",
url: "@Url.Action("ExportToExcel", "Profile")",
data: { "id": id },
dataType: "json"
success: function () {
window.location = '@Url.Action("ExportExcel", "Profile")?id='+id;
}
});
}
For Duplicate headers received from the server
Duplicate headers received from server
数据将在 AJAX GET 请求中转换为查询字符串;只需使用 jQuery 参数函数自己做:
$('#excel').on('click',function(){
var query = {
location: $('#location').val(),
area: $('#area').val(),
booth: $('#booth').val()
}
var url = "{{URL::to('downloadExcel_location_details')}}?" + $.param(query)
window.location = url;
});
在触发完全 post 返回之前无法下载文件。以下是您的操作方法: 您的 ExportToExcelx 函数将在 TempData 对象中保存文件,如下所示:
TempData["fileHandle"] = s.ToArray();
而不是return正在查看return临时数据标识符"fileHandle"和文件名,如下所示:
return Json(new { fileHandle = "fileHandle", FileName = "file.xls" }, JsonRequestBehavior.AllowGet);
所以你修改后的函数是这样的:
public JsonResult ExportToExcelx()
{
var products = new System.Data.DataTable("teste");
products.Columns.Add("col1", typeof(int));
products.Columns.Add("col2", typeof(string));
products.Rows.Add(1, "product 1");
products.Rows.Add(2, "product 2");
products.Rows.Add(3, "product 3");
products.Rows.Add(4, "product 4");
products.Rows.Add(5, "product 5");
products.Rows.Add(6, "product 6");
products.Rows.Add(7, "product 7");
var grid = new GridView();
grid.DataSource = products;
grid.DataBind();
Response.ClearContent();
Response.Buffer = true;
Response.AddHeader("content-disposition", "attachment; filename=MyExcelFile.xls");
Response.ContentType = "application/ms-excel";
Response.Charset = "";
StringWriter sw = new StringWriter();
HtmlTextWriter htw = new HtmlTextWriter(sw);
grid.RenderControl(htw);
//Response.Output.Write(sw.ToString());
//Response.Flush();
//Response.End();
// =============
//Open a memory stream that you can use to write back to the response
byte[] byteArray = Encoding.ASCII.GetBytes(sw.ToString());
MemoryStream s = new MemoryStream(byteArray);
TempData["fileHandle"] = s.ToArray();
//StreamReader sr = new StreamReader(s, Encoding.ASCII);
//Write the stream back to the response
Response.Write(sr.ReadToEnd());
Response.End();
return Json(new { fileHandle = "fileHandle", FileName = "file.xls" }, JsonRequestBehavior.AllowGet);
// return View("MyView");
}
现在您需要控制器中的另一个功能来下载如下文件:
[HttpGet]
public virtual ActionResult Download(string fileHandle, string fileName)
{
if (TempData[fileHandle] != null)
{
byte[] data = TempData[fileHandle] as byte[];
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
return new EmptyResult();
}
}
成功调用 ExportToExcelx 函数后,您的 ajax 调用将调用下载函数,如下所示:
$.ajax({
type: 'GET',
cache: false,
url: '/url',
success: function (data) {
window.location = '/url/Download?fileHandle=' + data.fileHandle
+ '&filename=' + data.FileName; //call download function
},
error: function (e) {
//handle error
}
下载函数然后将 return 文件。
希望对您有所帮助。
我在这里遇到过类似的问题,它也通过动态按钮解决了。我只需要在我的请求中包含一个 responseType:'blob'
。
并获得对按钮的响应:
var link = document.createElement('a');
link.href = window.URL.createObjectURL(response.data);
link.download='filename.xlsx';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
我的控制器写入输出流并生成 "application/xls"
response.setContentType("application/xls");
response.setHeader("Content-disposition", "attachment;");
response.getOutputStream().write(content);
首先,我不会用GridView生成excel。尽管是 "easy",它不会生成实际的 excel 文件,而是生成一个带有 xls 扩展名的 html 文件:
<div>
<table cellspacing="0" rules="all" border="1" style="border-collapse:collapse;">
<tr>
<th scope="col">col1</th><th scope="col">col2</th>
</tr><tr>
<td>1</td><td>product 1</td>
</tr><tr>
<td>2</td><td>product 2</td>
</tr><tr>
<td>3</td><td>product 3</td>
</tr><tr>
<td>4</td><td>product 4</td>
</tr><tr>
<td>5</td><td>product 5</td>
</tr><tr>
<td>6</td><td>product 6</td>
</tr><tr>
<td>7</td><td>product 7</td>
</tr>
</table>
</div>
这很烦人(而且不专业)。 如果您不受限于旧 excel 版本 - xls - 但可以使用最新的文件格式 xlsx,我宁愿使用 DocumentFormat.OpenXml nuget 包或其他 packages/libraries for excel 一代。 老实说,DocumentFormat.OpenXml 功能强大但使用起来有点乏味,当您有很多列并且只有 objects 的平面列表要报告时。 如果您使用的是 .NET Framework(不是 Dotnet Core),您可以尝试 CsvHelper.Excel nuget 包。用法非常简单。您的 ExportToExcel 方法将变为:
public ActionResult ExportToExcel(string id)
{
// TODO: Replace with correct products retrieving logic using id input
var products = new [] {
{ col1 = 1, col2 = "product 1" },
{ col1 = 2, col2 = "product 2" },
{ col1 = 3, col2 = "product 3" },
{ col1 = 4, col2 = "product 4" },
{ col1 = 5, col2 = "product 5" },
{ col1 = 6, col2 = "product 6" },
{ col1 = 7, col2 = "product 7" },
{ col1 = 1, col2 = "product 1" },
{ col1 = 1, col2 = "product 1" },
};
var ms = new MemoryStream();
var workbook = new XLWorkbook();
using (var writer = new CsvWriter(new ExcelSerializer(workbook)))
{
writer.WriteRecords(products);
}
workbook.SaveAs(ms);
ms.Flush();
ms.Seek(0, SeekOrigin.Begin);
return File(ms, MimeMapping.GetMimeMapping("file.xlsx"), $"MyExcelFile.xlsx");
}
另一个非常强大的包是 EPPlus,它允许您加载数据表(参见:https://whosebug.com/a/53957999/582792)。
来到 AJAX 部分,嗯...我认为您根本不需要它:一旦您将位置设置为新的 ExportToExcel 操作,它应该只下载文件。 假设您使用 Bootstrap 3,对于 collection 中的每个项目,您可以:
<a href="@Url.Action("ExportToExcel", "Profile", new {id=item.Id})" class="btn btn-info">
<i class="glyphicon glyphicon-download-alt" />
</a>
以下是我如何使它适用于 PDF。 Excel 下载应该差不多
$.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();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
或使用 download.js
$.ajax({
url: '<URL_TO_FILE>',
success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});