使用OpenXML SDK w / ASP.NET流式传输内存Word文档导致“损坏”文档

时间:2012-05-19 17:44:17

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

我无法将我创建的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,但没有运气。

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

那些偶然发现此问题的人的解决方案:

using的{​​{1}}指令中,您必须致电:

WordProcessingDocument

另外,为了正确地传输wordDocument.Save(); ,请在外部使用块中使用它:

MemoryStream

enter image description here enter image description here

7 个答案:

答案 0 :(得分:7)

使用CopyTo代替,WriteTo中存在一个错误,当目标流不支持一次编写所有内容时,它会无法写入缓冲区的全部内容。

答案 1 :(得分:2)

我相信您的ContentType值不正确;这是Word 97 - 2003格式。将其更改为:

application/vnd.openxmlformats-officedocument.wordprocessingml.document

并查看是否可以解决问题。

答案 2 :(得分:2)

我复制并粘贴了你的代码并注意到:“wordDocument.close();” clausule丢失,添加它并且它工作(我在Asp.NET MVC做了一个动作)

答案 3 :(得分:2)

作为.NET Framework 3.5及更低版本的变体。此版本的框架在类CopyTo中没有方法Stream。因此,方法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

找到了示例

答案 4 :(得分:1)

(这使用的是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");

`

一种超级简单的替代方法/解决方法是将您的流简单地转换为byte [],如下所示,这将产生一个有效的文档docx

                MemoryStream updateStream = new MemoryStream();
                wordDocument.Save();
                wordDocument.Clone(updateStream);


                return File(updateStream.ToArray(), "application/vnd.openxmlformats-officedocument.wordprocessingml.document");

答案 5 :(得分:0)

要扩展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();

答案 6 :(得分:0)

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

第二,处理IN-MEM的文档不适用于所有选项(例如,您不能在文档中插入块。必须使用文件模式)。

下面是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");

}