2014-01-21 122 views
3

我從OpenXML的MemoryStream出現問題。我成功地打開了一個Word文件,通過HttpResponse進行更改並下載它,如果我使用單一方法完成所有步驟的話。C#從OpenXML返回內存流導致損壞的Word文件

但是,如果我試圖通過返回MemoryStream在兩個不同的類(或方法)中做到這一點,我得到一個損壞的word文件。我想過沖洗或緩衝區問題,但我找不到解決方案。

這裏是工作代碼:

public void FillTemplateOpenXmlWord(HttpResponse response) 
    { 
     string filePath = @"c:\template.docx"; 
     byte[] filebytes = File.ReadAllBytes(filePath); 

     using (MemoryStream stream = new MemoryStream(filebytes)) 
     { 
      using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, true)) 
      { 
       // do some changes 
       ... 
       myDoc.MainDocumentPart.Document.Save(); 
      } 

      string docx = "docx"; 
      response.Clear(); 
      response.ClearHeaders(); 
      response.ClearContent(); 
      response.AddHeader("content-disposition", "attachment; filename=\"" + docx + ".docx\""); 
      response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; 
      response.ContentEncoding = Encoding.GetEncoding("ISO-8859-1"); 
      stream.Position = 0; 
      stream.CopyTo(response.OutputStream); 
      response.End(); 
     } 
    } 

這裏的非工作代碼:

public void OpenFile(HttpResponse response) 
    { 
     MemoryStream stream = this.FillTemplateOpenXmlWord(); 

     string docx = "docx"; 
     response.Clear(); 
     response.ClearHeaders(); 
     response.ClearContent(); 
     response.AddHeader("content-disposition", "attachment; filename=\"" + docx + ".docx\""); 
     response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; 
     response.ContentEncoding = Encoding.GetEncoding("ISO-8859-1"); 
     stream.Position = 0; 
     stream.CopyTo(response.OutputStream); 
     response.End(); 
    } 

    public MemoryStream FillTemplateOpenXmlWord() 
    { 
     string filePath = @"c:\template.docx"; 
     byte[] filebytes = File.ReadAllBytes(filePath); 

     using (MemoryStream stream = new MemoryStream(filebytes)) 
     { 
      using (WordprocessingDocument myDoc = WordprocessingDocument.Open(stream, true)) 
      { 
       // do some changes 
       ... 
       myDoc.MainDocumentPart.Document.Save(); 
      } 

      return stream; 
     } 
    } 

任何想法?

謝謝

回答

1

看起來像流正在關閉,當你返回。它在一個使用塊中。一旦filltemplate程序結束就不會關閉內存流?

+0

我剛剛發現了同樣的事情。當我得到答案時,我非常接近編輯我的問題並給出解決方案。 你說得對!我在使用條款中返回一個流。退貨不會創建副本,而是創建參考。所以,在調用方法中,我得到了一個不在using子句中的流的引用。該流被丟棄,內容不再可用。 謝謝 – DotNetBeliever

0

gashac發佈的答案沒有描述通過不在流上調用dispose將會遇到的問題。

不處理存儲器流導致內存泄漏(與「使用條款」相同)。

內存流將數據保存在內存中,而文件流將數據保留在硬盤上。

解決方案:

保存存儲器流入一個字節數組,配置內存流並返回字節組。

如何返回字節組,而不是流

請參見下面的線程返回一個文件作爲字節組: HttpResponseMessage Content won't display PDF

2

下面是我使用的是什麼,從內存流產生的OpenXML文件。在這種情況下,它會根據服務器上的模板生成XLSX文件,但它應該與其他OpenXml格式類似。

控制器動作:

public class ExportController : Controller 
{ 
    public FileResult Project(int id) 
    { 
     var model = SomeDateModel.Load(id); 
     ProjectExport export = new ProjectExport(); 
     var excelBytes = export.Export(model); 
     FileResult fr = new FileContentResult(excelBytes, "application/vnd.ms-excel") 
     { 
      FileDownloadName = string.Format("Export_{0}_{1}.xlsx", DateTime.Now.ToString("yyMMdd"), model.Name) 
     }; 

     return fr; 
    } 
} 

// Helper類

public class ProjectExport 
{ 
    private WorkbookPart workbook; 
    private Worksheet ws; 

    public byte[] Export(SomeDateModel model) 
    { 
     var template = new FileInfo(HostingEnvironment.MapPath(@"~\Export\myTemplate.xlsx")); 
     byte[] templateBytes = File.ReadAllBytes(template.FullName); 

     using (var templateStream = new MemoryStream()) 
     { 
      templateStream.Write(templateBytes, 0, templateBytes.Length); 
      using (var excelDoc = SpreadsheetDocument.Open(templateStream, true)) 
      { 
       workbook = excelDoc.WorkbookPart; 
       var sheet = workbook.Workbook.Descendants<Sheet>().First(); 

       ws = ((WorksheetPart)workbook.GetPartById(sheet.Id)).Worksheet; 

       sheet.Name = model.Name; 
       // Here write some other stuff for setting values in cells etc... 
      } 
      templateStream.Position = 0; 
      var result = templateStream.ToArray(); 
      templateStream.Flush(); 

      return result; 
     } 
    }