I have a large(ish) form in MVC.

我需要能够生成一个excel文件,其中包含来自该表单子集的数据.

The tricky bit is that this shouldn't affect the rest of the form and so I want to do it via AJAX. I've come across a few questions on SO that seem to be related, but I can't quite work out what the answers mean.

This one seems the closest to what I'm after: asp-net-mvc-downloading-excel - but I'm not sure I understand the response, and it is a couple years old now. I also came across another article (can't find it anymore) about using an iframe to handle the file download, but I'm not sure how to get this working with MVC.

如果我返回一个完整的帖子,我的excel文件返回得很好,但是我不能让它在MVC中与Ajax一起工作.

推荐答案

您不能通过AJAX调用直接返回要下载的文件,因此,另一种方法是使用AJAX调用将相关数据发布到服务器.然后,您可以使用服务器端代码来创建Excel文件(我建议使用EPPlus或NPOI来实现这一点,尽管听起来好像您的这个部分正在工作).

UPDATE September 2016

我的原始答案(如下)已经有3年多的历史了,所以我想我会更新,因为我在通过AJAX下载文件时不再在服务器上创建文件.然而,我保留了原始答案,因为它可能仍然有一些用途,这取决于您的具体要求.

在我的MVC应用程序中,一个常见的场景是通过具有一些用户配置的报告参数(日期范围、过滤器等)的网页进行报告.当用户指定了将其发布到服务器的参数后,将生成报告(例如,一个Excel文件作为输出),然后我将生成的文件作为字节数组存储在具有唯一引用的TempData bucket中.此引用作为Json结果传递回我的AJAX函数,该函数随后重定向到单独的控制器操作,以从TempData中提取数据并下载到最终用户浏览器.

To give this more detail, assuming you have a MVC View that has a form bound to a Model class, lets call the Model ReportVM.

First, a controller action is required to receive the posted model, an example would be:

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

The controller action to handle the downloading of the file:

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

One other change that could easily be accommodated if required is to pass the MIME Type of the file as a third parameter so that the one Controller action could correctly serve a variety of output file formats.

This removes any need for any physical files to created and stored on the server, so no housekeeping routines required and once again this is seamless to the end user.

Note, the advantage of using TempData rather than Session is that once TempData is read the data is cleared so it will be more efficient in terms of memory usage if you have a high volume of file requests. See TempData Best Practice.

ORIGINAL Answer

您不能通过AJAX调用直接返回要下载的文件,因此,另一种方法是使用AJAX调用将相关数据发布到服务器.然后,您可以使用服务器端代码来创建Excel文件(我建议使用EPPlus或NPOI来实现这一点,尽管听起来好像您的这个部分正在工作).

Once the file has been created on the server pass back the path to the file (or just the filename) as the return value to your AJAX call and then set the JavaScript window.location to this URL which will prompt the browser to download the file.

From the end users perspective, the file download operation is seamless as they never leave the page on which the request originates.

下面是一个简单的人为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;
    }
});
  • 参数是您的代码将在其中创建Excel文件的控制器/操作方法.
  • data参数包含将从表单中提取的json数据.
  • returnValue将是新创建的Excel文件的文件名.
  • window.location命令重定向到实际返回文件供下载的控制器/操作方法.

下载操作的示例控制器方法为:

[HttpGet]
public virtual ActionResult Download(string file)
{   
  string fullPath = Path.Combine(Server.MapPath("~/MyFiles"), file);
  return File(fullPath, "application/vnd.ms-excel", file);
}

Jquery相关问答推荐

在 Laravel 中使用 jQuery post 按相关值过滤 Select 选项,如何显示从控制器返回的数据?

禁用提交按钮,直到所有字段都有值

如何使用jQuery删除父元素

使用 jQuery 为 box-shadow 设置动画的正确方法

将数据传递给 jQuery UI 对话框

Jquery,清除/清空 tbody 元素的所有内容?

为什么 Chrome 会忽略本地 jQuery cookie?

未捕获的 TypeError:data.push 不是函数

使用 JQuery 获取触发事件的元素的类

隐藏 div 但保留空白

捕获在页面任意位置按下的 Enter 键

jquery在加载时获取iframe内容的高度

使用 AJAX 加载 Bootstrap 弹出内容.这可能吗?

Jquery:如何判断元素是否具有某些 css 类/样式

Underscore.js 和 jQuery 互补吗?

PHPStorm IDE 中低效的 jQuery 使用警告

jquery - 从一个非常大的表中删除所有行的最快方法

如何淡出显示:inline-block

jquery $(window).height() 正在返回文档高度

为什么我应该创建异步 WebAPI 操作而不是同步操作?