Search code examples
c#.net-corems-wordopenxmlmemorystream

Open XML WordprocessingDocument with MemoryStream is 0KB


I am trying to learn how to use with Microsoft's Open XML SDK. I followed their steps on how to create a Word document using a FileStream and it worked perfectly. Now I want to create a Word document but only in memory, and wait for the user to specify whether they would like to save the file or not.

This document by Microsoft says how to deal with in-memory documents using MemoryStream, however, the document is first loaded from an existing file and "dumped" into a MemorySteam. What I want is to create a document entirely in memory (not based on a file in a drive). What I thought would achieve that was this code:

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

But the result is a file that is 0KB and that can't be read by Word. At first I thought it was because of the size of the MemoryStream so I provided it with an initial size of 1024 but the results were the same. On the other hand if I change the MemoryStream for a FileStreamit works perfectly.

My question is whether what I want to do is possible, and if so, how? I guess it must be possible, just not how I'm doing it. If it isn't possible what alternative do I have?


Solution

  • There's a couple of things going on here:

    First, unlike Microsoft's sample, I was nesting the using block code that writes the file to disk inside the block that creates and modifies the file. The WordprocessingDocument gets saved to the stream until it is disposed or when the Save() method is called. The WordprocessingDocument gets disposed automatically when reaching the end of it's using block. If I had not nested the third using statement, thus reaching the end of the second using statement before trying to save the file, I would have allowed the document to be written to the MemoryStream- instead I was writing a still empty stream to disk (hence the 0KB file).

    I suppose calling Save()might have helped, but it is not supported by .Net core (which is what I'm using). You can check whether Save()is supported on you system by checking 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; }
    


    So the code ended up being almost identical to Microsoft's code except I don't read any files beforehand, rather I just begin with an empty 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);
        }
    }
    

    Also you don't need to reopen the document before saving it, but if you do remember to use Open() instead of Create() because Create() will empty the MemoryStream and you'll also end with a 0KB file.