【问题标题】:Streaming In Memory Word Document using OpenXML SDK w/ASP.NET results in "corrupt" document使用带有 ASP.NET 的 OpenXML SDK 在内存 Word 文档中流式传输会导致“损坏”文档
【发布时间】:2012-05-19 17:44:17
【问题描述】:

我无法将我即时创建的 Word 文档流式传输到浏览器。我不断收到来自 Microsoft Word 的消息,指出该文档已损坏。

当我通过控制台应用程序运行代码并从图片中删除 ASP.NET 时,文档会正确生成,没有任何问题。我相信一切都以写下文件为中心。

这是我的代码:

using (MemoryStream mem = new MemoryStream())
{
    // Create Document
    using (WordprocessingDocument wordDocument = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
    {
        // Add a main document part. 
        MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();

        new Document(new Body()).Save(mainPart);

        Body body = mainPart.Document.Body;
        body.Append(new Paragraph(new Run(new Text("Hello World!"))));

        mainPart.Document.Save();
        // Stream it down to the browser

        // THIS IS PROBABLY THE CRUX OF THE MATTER <---
        Response.AppendHeader("Content-Disposition", "attachment;filename=HelloWorld.docx");
        Response.ContentType = "application/vnd.ms-word.document";
        mem.WriteTo(Response.OutputStream);
        Response.End();
    }
}

我有很多looked links - 但没有什么能很好地工作。我很多人使用MemoryStream.WriteTo,有些人使用BinaryWrite——在这一点上,我不确定正确的方法是什么。我也尝试过更长的内容类型,即application/vnd.openxmlformats-officedocument.wordprocessingml.document,但没有运气。

一些屏幕截图 - 即使您尝试恢复,您也会得到相同的“部分丢失或无效”

遇到此问题的人的解决方案:

WordProcessingDocumentusing 指令内,您必须调用:

wordDocument.Save();

还要正确地流式传输MemoryStream,在外部 using 块中使用它:

Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
Response.AppendHeader("Content-Disposition", "attachment;filename=HelloWorld.docx");
mem.Position = 0;
mem.CopyTo(Response.OutputStream);
Response.Flush();
Response.End();

【问题讨论】:

  • 怎么添加wordDocument.Save(); ?我试过了,唯一可能接近它的代码是 .Close() ,这对我不起作用。

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


【解决方案1】:

改用CopyToWriteTo 中存在一个错误,当目标流不支持一次性写入所有内容时,无法写入缓冲区的全部内容。

【讨论】:

    【解决方案2】:

    作为 .NET Framework 3.5 及更低版本的变体。这个版本的框架在Stream 类中没有方法CopyTo。因此,方法WriteTo 被替换为下一个代码:

    byte[] arr = documentStream.ToArray();
    fileStream.Write(arr, 0, arr.Length);
    

    示例由http://blogs.msdn.com/b/mcsuksoldev/archive/2010/04/09/creating-a-new-microsoft-word-document-from-a-template-using-openxml.aspx找到

    【讨论】:

      【解决方案3】:

      我认为您的 ContentType 值不正确;这适用于 Word 97 - 2003 格式。将其更改为:

      application/vnd.openxmlformats-officedocument.wordprocessingml.document
      

      看看能不能解决问题。

      【讨论】:

        【解决方案4】:

        我复制并粘贴了您的代码,发现 :"wordDocument.close();" clausule 不见了,添加了它并且它工作了(我在 Asp.NET MVC 中做了一个动作)

        【讨论】:

          【解决方案5】:

          (这是使用 OpenXML SDK v 2.10.0 和 .Net Core v2.2)

          我知道这个问题已经得到解答,但我想放弃另一个选择。像下面这样尝试在 File() 中发回流会导致文档损坏,这是正确的:

                          MemoryStream updateStream = new MemoryStream();
                          wordDocument.Save();
                          wordDocument.Clone(updateStream);
          
          
                          return File(updateStream, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
          

          `

          一个超级简单的替代方案/解决方法是简单地将您的流转换为如下所示的字节[],它将产生一个有效的单词 docx

                          MemoryStream updateStream = new MemoryStream();
                          wordDocument.Save();
                          wordDocument.Clone(updateStream);
          
          
                          return File(updateStream.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
          

          【讨论】:

            【解决方案6】:

            为了扩展 Rodion 的答案并匹配问题中使用的变量,这对我有用:

            Response.ContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            Response.AppendHeader("Content-Disposition", "attachment;filename=HelloWorld.docx");
            mem.Position = 0;
            byte[] arr = mem.ToArray();
            Response.BinaryWrite(arr);
            Response.Flush();
            Response.End();
            

            【讨论】:

              【解决方案7】:

              首先,始终包含 Content-Length。如果浏览器不知道 http 响应正文的长度,则连接保持打开状态(保持活动状态)。如果没有其他方法,将内容保存到临时文件(关闭时删除选项),然后获取内容长度。

              其次,文档处理IN-MEM并非对所有选项都有效(例如不能在文档中插入chunk,必须使用文件模式)。

              以下是 aspnet 核心的示例:

              [HttpPost]
              public async Task<ActionResult<MopedResponse>> PostAsync(IFormCollection collection)
              {
              
                  string pathTemp = Path.GetTempFileName(); // get the temp file name
              
                  // create or process the word file
              
                  // reopen it for serving
              
                  FileStream merged = new FileStream(pathTemp, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose);
              
                  System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
                  {
                      FileName = "parafa.docx",
                      Inline = true  // false = prompt the user for downloading;  true = browser to try to show the file inline
                  };
                  Response.Headers.Add("Content-Disposition", cd.ToString());
                  Response.Headers.Add("Content-Length", merged.Length.ToString());
              
                  return File(merged, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "mywordFile1.docx");
              
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2014-02-10
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-12-06
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多