21

我无法将我即时创建的 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();
    }
}

查看了很多链接——但没有一个很有效。我有很多人使用MemoryStream.WriteTo和一些使用BinaryWrite- 在这一点上,我不确定正确的方法是什么。我也尝试过更长的内容类型,即application/vnd.openxmlformats-officedocument.wordprocessingml.document但没有运气。

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

对于那些偶然发现这个问题的人的解决方案:

在 的using指令中WordProcessingDocument,您必须调用:

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();

在此处输入图像描述 在此处输入图像描述

4

7 回答 7

8

相反CopyToWriteTo当目标流不支持一次性写入所有内容时,会导致无法写入缓冲区的全部内容。

于 2012-05-19T19:51:34.997 回答
3

作为 .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找到

于 2013-09-26T09:30:09.523 回答
2

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

于 2013-02-19T17:48:45.033 回答
2

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

application/vnd.openxmlformats-officedocument.wordprocessingml.document

看看是否能解决问题。

于 2012-05-19T21:27:10.527 回答
2

(这是使用 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");
于 2020-01-16T03:24:17.677 回答
0

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

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

下面是 aspnet core 的示例:

[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");

}
于 2018-12-03T09:35:56.803 回答
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();
于 2015-07-20T18:21:53.260 回答