【问题标题】:How can I present a file for download from an MVC controller?如何呈现文件以从 MVC 控制器下载?
【发布时间】:2010-10-18 08:36:13
【问题描述】:

在 WebForms 中,我通常会使用这样的代码让浏览器显示一个“下载文件”弹出窗口,其中包含任意文件类型(如 PDF)和文件名:

Response.Clear()
Response.ClearHeaders()
''# Send the file to the output stream
Response.Buffer = True

Response.AddHeader("Content-Length", pdfData.Length.ToString())
Response.AddHeader("Content-Disposition", "attachment; filename= " & Server.HtmlEncode(filename))

''# Set the output stream to the correct content type (PDF).
Response.ContentType = "application/pdf"

''# Output the file
Response.BinaryWrite(pdfData)

''# Flushing the Response to display the serialized data
''# to the client browser.
Response.Flush()
Response.End()

如何在 ASP.NET MVC 中完成相同的任务?

【问题讨论】:

    标签: asp.net-mvc download


    【解决方案1】:

    你应该看看 Controller 的 File 方法。这正是它的用途。它返回 FilePathResult 而不是 ActionResult。

    【讨论】:

      【解决方案2】:

      从您的操作中返回 FileResultFileStreamResult,具体取决于文件是否存在或您是动态创建的。

      public ActionResult GetPdf(string filename)
      {
          return File(filename, "application/pdf", Server.UrlEncode(filename));
      }
      

      【讨论】:

      • 这需要文件名上的文件扩展名,否则它将完全忽略文件名和内容类型,只是尝试将文件流式传输到浏览器。如果浏览器在强制下载时无法识别内容类型(即八位字节流),它也只会使用网页名称,并且根本没有扩展名。
      【解决方案3】:

      使用 .ashx 文件类型并使用相同的代码

      【讨论】:

        【解决方案4】:

        马努南,

        您可以这样做以返回 FileStream:

        /// <summary>
        /// Creates a new Excel spreadsheet based on a template using the NPOI library.
        /// The template is changed in memory and a copy of it is sent to
        /// the user computer through a file stream.
        /// </summary>
        /// <returns>Excel report</returns>
        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult NPOICreate()
        {
            try
            {
                // Opening the Excel template...
                FileStream fs =
                    new FileStream(Server.MapPath(@"\Content\NPOITemplate.xls"), FileMode.Open, FileAccess.Read);
        
                // Getting the complete workbook...
                HSSFWorkbook templateWorkbook = new HSSFWorkbook(fs, true);
        
                // Getting the worksheet by its name...
                HSSFSheet sheet = templateWorkbook.GetSheet("Sheet1");
        
                // Getting the row... 0 is the first row.
                HSSFRow dataRow = sheet.GetRow(4);
        
                // Setting the value 77 at row 5 column 1
                dataRow.GetCell(0).SetCellValue(77);
        
                // Forcing formula recalculation...
                sheet.ForceFormulaRecalculation = true;
        
                MemoryStream ms = new MemoryStream();
        
                // Writing the workbook content to the FileStream...
                templateWorkbook.Write(ms);
        
                TempData["Message"] = "Excel report created successfully!";
        
                // Sending the server processed data back to the user computer...
                return File(ms.ToArray(), "application/vnd.ms-excel", "NPOINewFile.xls");
            }
            catch(Exception ex)
            {
                TempData["Message"] = "Oops! Something went wrong.";
        
                return RedirectToAction("NPOI");
            }
        }
        

        【讨论】:

          【解决方案5】:

          强制下载 PDF 文件,而不是由浏览器的 PDF 插件处理:

          public ActionResult DownloadPDF()
          {
              return File("~/Content/MyFile.pdf", "application/pdf", "MyRenamedFile.pdf");
          }
          

          如果您想让浏览器按其默认行为(插件或下载)处理,只需发送两个参数。

          public ActionResult DownloadPDF()
          {
              return File("~/Content/MyFile.pdf", "application/pdf");
          }
          

          您需要使用第三个参数在浏览器对话框中指定文件的名称。

          更新:Charlino 是对的,当传递第三个参数(下载文件名)Content-Disposition: attachment; 被添加到 Http 响应标头中。我的解决方案是将application\force-download 作为mime 类型发送,但这会产生下载文件名的问题,因此需要第三个参数来发送一个好的文件名,因此无需强制下载 em>。

          【讨论】:

          • 从技术上讲,这不是正在发生的事情。从技术上讲,当您添加第三个参数时,MVC 框架会添加标头 content-disposition: attachment; filename=MyRenamedFile.pdf - 这就是强制下载的原因。我建议你把 MIME 类型放回application/pdf
          • 谢谢 Charlino,我没有意识到第三个参数是这样做的,我以为只是为了更改文件名。
          【解决方案6】:

          您可以在 Razor 或 Controller 中执行相同操作,就像这样......

          @{
              //do this on the top most of your View, immediately after `using` statement
              Response.ContentType = "application/pdf";
              Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");
          }
          

          或者在控制器中..

          public ActionResult Receipt() {
              Response.ContentType = "application/pdf";
              Response.AddHeader("Content-Disposition", "attachment; filename=receipt.pdf");
          
              return View();
          }
          

          我在 Chrome 和 IE9 中试过这个,都在下载 pdf 文件。

          我可能应该添加我正在使用RazorPDF 来生成我的 PDF。这是一篇关于它的博客:http://nyveldt.com/blog/post/Introducing-RazorPDF

          【讨论】:

            【解决方案7】:

            虽然标准操作结果 FileContentResult 或 FileStreamResult 可用于下载文件,但为了可重用性,创建自定义操作结果可能是最佳解决方案。

            作为示例,让我们创建一个自定义操作结果,用于将数据动态导出到 Excel 文件以供下载。

            ExcelResult 类继承了抽象的 ActionResult 类并重写了 ExecuteResult 方法。

            我们使用 FastMember 包从 IEnumerable 对象创建 DataTable,并使用 ClosedXML 包从 DataTable 创建 Excel 文件。

            public class ExcelResult<T> : ActionResult
            {
                private DataTable dataTable;
                private string fileName;
            
                public ExcelResult(IEnumerable<T> data, string filename, string[] columns)
                {
                    this.dataTable = new DataTable();
                    using (var reader = ObjectReader.Create(data, columns))
                    {
                        dataTable.Load(reader);
                    }
                    this.fileName = filename;
                }
            
                public override void ExecuteResult(ControllerContext context)
                {
                    if (context != null)
                    {
                        var response = context.HttpContext.Response;
                        response.Clear();
                        response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                        response.AddHeader("content-disposition", string.Format(@"attachment;filename=""{0}""", fileName));
                        using (XLWorkbook wb = new XLWorkbook())
                        {
                            wb.Worksheets.Add(dataTable, "Sheet1");
                            using (MemoryStream stream = new MemoryStream())
                            {
                                wb.SaveAs(stream);
                                response.BinaryWrite(stream.ToArray());
                            }
                        }
                    }
                }
            }
            

            在Controller中使用自定义的ExcelResult动作结果如下

            [HttpGet]
            public async Task<ExcelResult<MyViewModel>> ExportToExcel()
            {
                var model = new Models.MyDataModel();
                var items = await model.GetItems();
                string[] columns = new string[] { "Column1", "Column2", "Column3" };
                string filename = "mydata.xlsx";
                return new ExcelResult<MyViewModel>(items, filename, columns);
            }
            

            由于我们使用 HttpGet 下载文件,因此创建一个没有模型和空布局的空视图。

            关于下载动态创建的文件的自定义操作结果的博文:

            https://acanozturk.blogspot.com/2019/03/custom-actionresult-for-files-in-aspnet.html

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-07-29
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多