【问题标题】:C# return memory stream from OpenXML resulting to a corrupted word fileC# 从 OpenXML 返回内存流,导致 Word 文件损坏
【发布时间】:2014-02-10 22:06:22
【问题描述】:

我对来自 OpenXML 的 MemoryStream 有疑问。如果我在一个方法中完成所有步骤,我可以成功打开一个 Word 文件、更改它并通过 HttpResponse 下载它。

但是,如果我尝试通过返回 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;
        }
    }

有什么想法吗?

谢谢

【问题讨论】:

    标签: c# .net ms-word openxml memorystream


    【解决方案1】:

    当您返回时,流似乎正在关闭。它在使用块中。一旦filltemplate过程结束,那不会关闭内存流吗?

    【讨论】:

    • 我刚刚发现了同样的事情。当我得到你的答案时,我非常接近编辑我的问题并给出解决方案。你是对的!我在 using 子句中返回一个流。返回不会创建副本,而是创建引用。因此,在调用者方法中,我得到了一个对不再在 using 子句中的流的引用。流被处理,内容不再可用。谢谢
    【解决方案2】:

    gashac 发布的答案并未描述您在流上不调用 dispose 会遇到的问题。

    不释放内存流会导致内存泄漏(与“using 子句”相同)。

    内存流将数据保存在内存中,而文件流将数据保存在硬盘中。

    解决方案:

    将内存流保存到字节数组中,释放内存流并返回字节数组。

    如何返回字节数组而不是流

    请参阅以下线程以将文件作为字节数组返回: HttpResponseMessage Content won't display PDF

    【讨论】:

      【解决方案3】:

      这是我用于从内存流生成 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;
          }
      }
      

      // 辅助类

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

      【讨论】:

      • 这很好用!这正是我的代码在内存文件上工作所需要的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多