Search code examples
c#asp.net-core-3.1wordprocessingml

Modifying WordprocessingDocument does not save changes


It's my first time using DocumentFormat.OpenXml so I have a noob question/problem.

Using help from this post: Save modified WordprocessingDocument to new file I wrote the code below to change some text (by tag) in an existing Word (.docx) document and then download as a new file.

I'm using Asp.Net Core 3.1 for a small web application.

byte[] result;
//source file
var path = Path.Combine(_webHostingEnvironment.WebRootPath, "templates\\MyReport.docx");
string documentText;

byte[] byteArray = System.IO.File.ReadAllBytes(path);
using (MemoryStream stream = new MemoryStream())
{
    stream.Write(byteArray, 0, (int)byteArray.Length);
    using (WordprocessingDocument doc = WordprocessingDocument.Open(stream, true))
    {
        using (StreamReader reader = new StreamReader(doc.MainDocumentPart.GetStream()))
        {
            documentText = reader.ReadToEnd();
        }

    documentText = documentText.Replace("company", "My Company ltd.");
    }
    result = stream.ToArray();
}

//download new file
return File(result, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "MyReport.docx");

I'm experiencing two problems:

  1. file downloaded is exactly the same as the source Word file. documentText.Replace seems to have no effect on the file being downloaded
  2. when I want to use a bit more "complex" tags within the source Word file, e.g. {company} it gets "split" in the documentText into separate words...

What am I doing wrong?


Solution

  • Apparently using StreamReader within WordprocessingDocument was wrong. Here is a working example in case anyone else is looking for a similar solution (with the help from this article: OfficeTalk: Working with In-Memory Open XML Documents):

    public virtual IActionResult ExportWord()
    {
        byte[] result;
    
        // path to where your template is stored
        var path = Path.Combine(_webHostingEnvironment.WebRootPath, "files\\template.docx");
    
        // key-value pairs of what you want to change
        Dictionary<string, string> dict = new Dictionary<string, string>
        {
            { "key1", "value1" },
            { "key2", "value2" },
            { "key3", "value3" },
            // etc.
        };
    
        // read template into stream
        byte[] byteArray = System.IO.File.ReadAllBytes(path);
        using (MemoryStream mem = new MemoryStream())
        {
            mem.Write(byteArray, 0, (int)byteArray.Length);
            using (WordprocessingDocument doc = WordprocessingDocument.Open(mem, true))
            {
                // Modify the document as necessary.
                var body = doc.MainDocumentPart.Document.Body;
                var paras = body.Elements<Paragraph>();
    
                foreach (var para in paras)
                {
                    foreach (var run in para.Elements<Run>())
                    {
                        foreach (var text in run.Elements<Text>())
                        {
                            foreach (var item in dict)
                            {
                                if (text.Text.Contains(item.Key))
                                {
                                    text.Text = text.Text.Replace(item.Key, item.Value);
                                }
                            }
                        }
                    }
                }
            }
    
            // At this point, the memory stream contains the modified document.
            result = mem.ToArray();
        }
    
        return File(result, "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "modified.docx");
    }