使用OpenXML从MVC控制器读取工作簿

我使用Open XML从MVC生成Excel文档。

代码如下:

public ActionResult ExcelReport() { var custId = 1234; //ToDo get from session when it is set using (MemoryStream mem = new MemoryStream()) { ExcelReportGenerator excel = new ExcelReportGenerator(); excel.CreateExcelDoc(mem); var reportName = string.Format("MyReport_{0}.xlsx", custId); return File(mem.ToArray(), System.Net.Mime.MediaTypeNames.Application.Octet, reportName); } } 

我的CreateExcelDoc方法如下所示:

 public void CreateExcelDoc(MemoryStream mem) { SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Create(mem, SpreadsheetDocumentType.Workbook); //SpreadsheetDocument spreadsheetDocument = SpreadsheetDocument.Open(mem, false); //Code removed for brevity worksheetPart.Worksheet.Save(); spreadsheetDocument.Close(); } 

使用创build方法,Excel工作表正在按预期进行下载。 但是,我想使工作簿只读,所以试图.Open方法传递的stream,并将isEditable设置为false,但我得到一个YSOD时,我打错了Exception Details: System.IO.FileFormatException: Archive file cannot be size 0.的控制器Exception Details: System.IO.FileFormatException: Archive file cannot be size 0.

一个简单的方法,使您的Excel只读是保护工作表(请参阅第2步链接) 。

这样做的OpenXML代码是每个表单一行:

 workSheet.Append(new SheetProtection() { Sheet = true, Objects = true, Scenarios = true }); 

我已经推出了使用这种技术的参考实现 MVC的新版本 。 SheetProtection似乎在Excel 2007中受支持,其中从Excel 2013开始只支持标记为Final。

调用SpreadsheetDocument.Open不会导致只读文件。 它只是以只读模式打开内存stream。 ( isEditable为false)这会lockingstream由其余代码更改。 这就解释了为什么您在添加该调用之前很可能会生成excel罚款,以及为什么在添加该调用之后您将获得空归档exception。 不要为此使用SpreadsheetDocument.Open

我不build议您尝试将Excel作为只读文件发送。 用户可以在任何操作系统上下载并重新命名,并根据需要更改文件权限。

我build议添加openXML代码, 将您的Excel标记为final 。 微软将这种function添加到Office文档中,因为它更正式地阻止用户对文件进行未来更改,而不是简单的文件权限。 对这些文件之一进行的任何更改都需要新的文件名。

推荐的OpenXml代码将生成一个自定义属性,所以当用户下载文件时,它将被标记为Final以阻止用户编辑。

以下是将添加自定义属性的方法:

 private void GenerateCustomFilePropertiesPart1Content(CustomFilePropertiesPart customFilePropertiesPart1) { Op.Properties properties2 = new Op.Properties(); properties2.AddNamespaceDeclaration("vt", "http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); Op.CustomDocumentProperty customDocumentProperty1 = new Op.CustomDocumentProperty() { FormatId = "{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", PropertyId = 2, Name = "_MarkAsFinal" }; Vt.VTBool vTBool1 = new Vt.VTBool(); vTBool1.Text = "true"; customDocumentProperty1.Append(vTBool1); properties2.Append(customDocumentProperty1); customFilePropertiesPart1.Properties = properties2; } 

我已经写了一个简单的MVC应用程序,生成一个空的Excel文件标记为最终作为参考实现。 代码托pipe在GitHub上 。 当你运行它时,点击最右边的菜单链接“下载Excel”。 这将下载一个名为沃尔特的工作簿,没有数据的工作簿的Excel。 excel将被标记为final。 自定义属性的代码是使用OpenXML生产力工具生成的。 祝你的项目好运。