我在MVC中有一个大(ish)形式。
我需要能够生成一个Excel文件,其中包含来自该表单子集的数据。
棘手的一点是,这不应影响表单的其余部分,因此我想通过AJAX进行操作。我遇到了一些关于SO的问题,这些问题似乎相关,但是我无法完全弄清楚答案的含义。
这似乎是我所追求的最接近的东西:asp-net-mvc-downloading- excel-但我不确定我是否理解响应,并且它已经存在了两年。我还遇到了另一篇有关使用iframe来处理文件下载的文章(找不到该文章了),但是我不确定如何在MVC中使用它。
如果我正在做完整的回发,但我的excel文件返回的很好,但是我无法在mvc中将其与AJAX一起使用。
您无法直接通过AJAX调用返回要下载的文件,因此,另一种方法是使用AJAX调用将相关数据发布到服务器。然后,您可以使用服务器端代码来创建Excel文件(我建议为此使用EPPlus或NPOI,尽管听起来好像您正在使用此部分)。
2016年9月更新
我的原始答案(如下)已有3年之久了,所以我想我将进行更新,因为通过AJAX下载文件时不再在服务器上创建文件,但是,我留下了原始答案,因为它可能仍会有所用,具体取决于您的具体要求。
在我的MVC应用程序中,常见的情况是通过网页进行报告,该网页具有一些用户配置的报告参数(日期范围,过滤器等)。当用户指定了将其发布到服务器的参数后,将生成报告(例如,将Excel文件作为输出),然后将结果文件作为字节数组TempData存储在具有唯一引用的存储桶中。此引用作为Json结果传递回我的AJAX函数,该函数随后重定向到单独的控制器操作,以从TempData最终用户浏览器中提取数据并下载到最终用户浏览器。
TempData
为了提供更多细节,假设您有一个MVC View,它具有绑定到Model类的形式,让我们调用Model ReportVM。
ReportVM
首先,需要一个控制器操作来接收发布的模型,例如:
public ActionResult PostReportPartial(ReportVM model){ // Validate the Model is correct and contains valid data // Generate your report output based on the model parameters // This can be an Excel, PDF, Word file - whatever you need. // As an example lets assume we've generated an EPPlus ExcelPackage ExcelPackage workbook = new ExcelPackage(); // Do something to populate your workbook // Generate a new unique identifier against which the file can be stored string handle = Guid.NewGuid().ToString(); using(MemoryStream memoryStream = new MemoryStream()){ workbook.SaveAs(memoryStream); memoryStream.Position = 0; TempData[handle] = memoryStream.ToArray(); } // Note we are returning a filename as well as the handle return new JsonResult() { Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" } }; }
将我的MVC表单发布到上面的控制器并接收响应的AJAX调用如下所示:
$ajax({ cache: false, url: '/Report/PostReportPartial', data: _form.serialize(), success: function (data){ var response = JSON.parse(data); window.location = '/Report/Download?fileGuid=' + response.FileGuid + '&filename=' + response.FileName; } })
控制器处理文件下载的操作:
[HttpGet] public virtual ActionResult Download(string fileGuid, string fileName) { if(TempData[fileGuid] != null){ byte[] data = TempData[fileGuid] as byte[]; return File(data, "application/vnd.ms-excel", fileName); } else{ // Problem - Log the error, generate a blank file, // redirect to another controller action - whatever fits with your application return new EmptyResult(); } }
如果需要,可以轻松进行的另一项更改是将文件的MIME类型作为第三个参数传递,以便一个Controller动作可以正确地提供多种输出文件格式。
这消除了在服务器上创建和存储任何物理文件的需要,因此不需要内务处理例程,这对最终用户而言也是无缝的。
请注意,使用TempData而不是使用的好处Session是,一旦TempData读取了数据,就将其清除,因此,如果您有大量文件请求,则在内存使用方面将更加有效。请参见TempData最佳实践。
Session
原始答案
在服务器上创建文件后,将文件路径(或只是文件名)作为AJAX调用的返回值,然后将JavaScript window.location设置为此URL,这将提示浏览器下载文件。
window.location
从最终用户的角度来看,文件下载操作是无缝的,因为他们永远不会离开发出请求的页面。
下面是一个简单的ajax调用示例,以实现此目的:
$.ajax({ type: 'POST', url: '/Reports/ExportMyData', data: '{ "dataprop1": "test", "dataprop2" : "test2" }', contentType: 'application/json; charset=utf-8', dataType: 'json', success: function (returnValue) { window.location = '/Reports/Download?file=' + returnValue; } });
下载操作的示例控制器方法为:
[HttpGet] public virtual ActionResult Download(string file) { string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file); return File(fullPath, "application/vnd.ms-excel", file); }