使用 MemoryStream 打开 XML WordprocessingDocument 为 0KB

lsa*_*eda 3 c# memorystream ms-word openxml .net-core

我正在尝试学习如何使用 Microsoft 的 Open XML SDK。我按照他们关于如何使用 a 创建 Word 文档的步骤进行操作FileStream,效果非常好。现在我想创建一个Word文档,但仅在内存中,并等待用户指定是否要保存该文件。

Microsoft 的这份文档说明了如何使用 处理内存中的文档MemoryStream,但是,该文档首先从现有文件加载并“转储”到MemorySteam. 我想要的是完全在内存中创建文档(而不是基于驱动器中的文件)。我认为可以实现这一点的是这段代码:

// This is almost the same as Microsoft's code except I don't
// dump any files into the MemoryStream
using (var mem = new MemoryStream())
{
    using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
    {
        doc.AddMainDocumentPart().Document = new Document();
        var body = doc.MainDocumentPart.Document.AppendChild(new Body());
        var paragraph = body.AppendChild(new Paragraph());
        var run = paragraph.AppendChild(new Run());
        run.AppendChild(new Text("Hello docx"));

        using (var file = new FileStream(destination, FileMode.CreateNew))
        {
            mem.WriteTo(file);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但结果是一个 0KB 的文件,并且 Word 无法读取该文件。起初我以为是因为 的大小,MemoryStream所以我给它提供了初始大小 1024,但结果是一样的。另一方面,如果我改变 a ,MemoryStreamFileStream就会完美地工作。

我的问题是我想做的事情是否可能,如果可以,怎样做?我想这一定是可能的,只是不是我这样做的方式。如果不可能,我还有什么选择?

lsa*_*eda 5

这里发生了一些事情:

首先,与 Microsoft 的示例不同,我将用于将using文件写入磁盘的块代码嵌套在创建和修改文件的块内。被WordprocessingDocument保存到流中,直到它被释放或Save()调用该方法时。WordprocessingDocument当到达块的末尾时,它们会被自动处理using。如果我没有嵌套第三个 using 语句,从而在using尝试保存文件之前到达第二个语句的末尾,我将允许将文档写入MemoryStream- 相反,我将一个仍然为空的流写入磁盘(因此0KB 文件)。

调用Save()可能会有所帮助,但 .Net core(我正在使用的)不支持它。您可以Save()通过检查来检查您的系统是否支持CanSave

/// <summary>
/// Gets a value indicating whether saving the package is supported by calling <see cref="Save"/>. Some platforms (such as .NET Core), have limited support for saving.
/// If <c>false</c>, in order to save, the document and/or package needs to be fully closed and disposed and then reopened.
/// </summary>
public static bool CanSave { get; }
Run Code Online (Sandbox Code Playgroud)


因此,代码最终与微软的代码几乎相同,只是我没有事先读取任何文件,而是从一个空的开始MemoryStream

using (var mem = new MemoryStream())
{
    using (var doc = WordprocessingDocument.Create(mem, WordprocessingDocumentType.Document, true))
    {
        doc.AddMainDocumentPart().Document = new Document();
        var body = doc.MainDocumentPart.Document.AppendChild(new Body());
        var paragraph = body.AppendChild(new Paragraph());
        var run = paragraph.AppendChild(new Run());
        run.AppendChild(new Text("Hello docx"));
    }

    using (var file = new FileStream(destination, FileMode.CreateNew))
    {
        mem.WriteTo(file);
    }
}
Run Code Online (Sandbox Code Playgroud)

另外,在保存文档之前,您不需要重新打开文档,但如果您确实记得使用Open()而不是,Create()因为Create()会清空文档MemoryStream,并且您还会以 0KB 文件结束。