使用开放式xml时如何修改文件流中的内容?

在下面的代码中,我将一些文件合并在一起,并将它们保存在test.docx文件中。但是,在合并每个文件之前,我想先替换一些用作占位符的内容控件的文本。有人可以告诉我该怎么做吗?

假设我在template2中有一个内容控件,它被称为placeholder1。使用文件流时如何在此占位符中添加文本?

string fileName = Path.Combine(@"Docs\templates","test.docx");
            for (int i = 1; i < 3; i++)
            {
                string filePath = Path.Combine(@"Docs\templates","report-Part" + i + ".docx");

                //using (MemoryStream ms = new MemoryStream())
                //{
                //ms.Write(templateFile,templateFile.Length);
                using (WordprocessingDocument myDoc = WordprocessingDocument.Open(fileName,true))
                {
                    MainDocumentPart mainPart = myDoc.MainDocumentPart;
                    string altChunkId = "AltChunkId" + Guid.NewGuid();
                    AlternativeFormatImportPart chunk = mainPart.AddAlternativeFormatImportPart(AlternativeFormatImportPartType.WordprocessingML,altChunkId);
                    using (FileStream fileStream = File.Open(filePath,FileMode.Open))
                    {
                        chunk.FeedData(fileStream);
                    }
                    //chunk.FeedData(ms);
                    AltChunk altChunk = new AltChunk();
                    altChunk.Id = altChunkId;

                    Paragraph paragraph2 = new Paragraph() { RsidParagraphAddition = "00BE27E7",RsidRunAdditionDefault = "00BE27E7" };

                    Run run2 = new Run();
                    Break break1 = new Break() { Type = BreakValues.Page };

                    run2.Append(break1);
                    paragraph2.Append(run2);
                    mainPart.Document.Body.Append(paragraph2);
                    var lastParagraph = mainPart.Document.Body.Elements<Paragraph>().Last();
                    mainPart.Document.Body.InsertAfter(altChunk,lastParagraph);
                    mainPart.Document.Save();
                    myDoc.Close();
                }
                //ms.Position = 0;
                ////ms.ToArray();
                //output = new byte[ms.ToArray().Length];
                //ms.Read(output,output.Length);
                //}
didy21dd 回答:使用开放式xml时如何修改文件流中的内容?

以下作为xUnit单元测试编写的示例代码说明了如何实现所需的功能。我添加了代码注释来解释执行的操作和原因。

    public class AltChunkAssemblyTests
    {
        // Sample template file names for unit testing purposes.
        private readonly string[] _templateFileNames =
        {
            "report-Part1.docx","report-Part2.docx","report-Part3.docx"
        };

        // Sample content maps for unit testing purposes.
        // Each Dictionary<string,string> represents data used to replace the
        // content of block-level w:sdt elements identified by w:tag values of
        // "firstTag" and "secondTag".
        private readonly List<Dictionary<string,string>> _contentMaps = new List<Dictionary<string,string>>
        {
            new Dictionary<string,string>
            {
                { "firstTag","report-Part1: First value" },{ "secondTag","report-Part1: Second value" }
            },new Dictionary<string,"report-Part2: First value" },"report-Part2: Second value" }
            },"report-Part3: First value" },"report-Part3: Second value" }
            }
        };

        [Fact]
        public void CanAssembleDocumentUsingAltChunks()
        {
            // Create some sample "templates" (technically documents) for unit
            // testing purposes.
            CreateSampleTemplates();

            // Create an empty result document.
            using WordprocessingDocument wordDocument = WordprocessingDocument.Create(
                "AltChunk.docx",WordprocessingDocumentType.Document);

            MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
            var body = new Body();
            mainPart.Document = new Document(body);

            // Add one w:altChunk element for each sample template,using the
            // sample content maps for mapping sample data to the content
            // controls contained in the templates.
            for (var index = 0; index < 3; index++)
            {
                if (index > 0) body.AppendChild(new Paragraph(new Run(new Break { Type = BreakValues.Page })));
                body.AppendChild(CreateAltChunk(_templateFileNames[index],_contentMaps[index],wordDocument));
            }
        }

        private void CreateSampleTemplates()
        {
            // Create a sample template for each sample template file names.
            foreach (string templateFileName in _templateFileNames)
            {
                CreateSampleTemplate(templateFileName);
            }
        }

        private static void CreateSampleTemplate(string templateFileName)
        {
            // Create a new Word document with paragraphs marking the start and
            // end of the template (for testing purposes) and two block-level
            // structured document tags identified by w:tag elements with values
            // "firstTag" and "secondTag" and values that are going to be
            // replaced by the ContentControlWriter during document assembly.
            using WordprocessingDocument wordDocument = WordprocessingDocument.Create(
                templateFileName,WordprocessingDocumentType.Document);

            MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();
            mainPart.Document =
                new Document(
                    new Body(
                        new Paragraph(
                            new Run(
                                new Text($"Start of template '{templateFileName}'"))),new SdtBlock(
                            new SdtProperties(
                                new Tag { Val = "firstTag" }),new SdtContentBlock(
                                new Paragraph(
                                    new Run(
                                        new Text("First template value"))))),new SdtBlock(
                            new SdtProperties(
                                new Tag { Val = "secondTag" }),new SdtContentBlock(
                                new Paragraph(
                                    new Run(
                                        new Text("Second template value"))))),new Paragraph(
                            new Run(
                                new Text($"End of template '{templateFileName}'")))));
        }

        private static AltChunk CreateAltChunk(
            string templateFileName,Dictionary<string,string> contentMap,WordprocessingDocument wordDocument)
        {
            // Copy the template file contents to a MemoryStream to be able to
            // update the content controls without altering the template file.
            using FileStream fileStream = File.Open(templateFileName,FileMode.Open);
            using var memoryStream = new MemoryStream();
            fileStream.CopyTo(memoryStream);

            // Open the copy of the template on the MemoryStream,update the
            // content controls,save the updated template back to the
            // MemoryStream,and reset the position within the MemoryStream.
            using (WordprocessingDocument chunkDocument = WordprocessingDocument.Open(memoryStream,true))
            {
                var contentControlWriter = new ContentControlWriter(contentMap);
                contentControlWriter.WriteContentControls(chunkDocument);
            }

            memoryStream.Seek(0,SeekOrigin.Begin);

            // Create an AlternativeFormatImportPart from the MemoryStream.
            string altChunkId = "AltChunkId" + Guid.NewGuid();
            AlternativeFormatImportPart chunk = wordDocument.MainDocumentPart.AddAlternativeFormatImportPart(
                AlternativeFormatImportPartType.WordprocessingML,altChunkId);

            chunk.FeedData(memoryStream);

            // Return the w:altChunk element to be added to the w:body element.
            return new AltChunk { Id = altChunkId };
        }
    }

我已经使用创建的ContentControlWriter类测试了代码,以回答有关如何create a new document from word template with multiple pages using documentformat.openxml的其他问题。效果很好。完整的代码可以在我的CodeSnippets GitHub存储库中找到。寻找AltChunkAssemblyTestsContentControlWriter

CreateSampleTemplates()方法创建三个示例文档。例如,report-Part1.docx的主要文档部分具有以下内容:

<?xml version="1.0" encoding="utf-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:body>
    <w:p>
      <w:r>
        <w:t>Start of template 'report-Part1.docx'</w:t>
      </w:r>
    </w:p>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="firstTag" />
      </w:sdtPr>
      <w:sdtContent>
        <w:p>
          <w:r>
            <w:t>First template value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="secondTag" />
      </w:sdtPr>
      <w:sdtContent>
        <w:p>
          <w:r>
            <w:t>Second template value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:p>
      <w:r>
        <w:t>End of template 'report-Part1.docx'</w:t>
      </w:r>
    </w:p>
  </w:body>
</w:document>

在组装后并且没有让Word再次保存文档,AltChunk.docx的主要文档部分如下所示:

<?xml version="1.0" encoding="utf-8"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
  <w:body>
    <w:altChunk r:id="AltChunkId81885280-e38d-4ffb-b8a3-38d96992c2eb" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
    <w:p>
      <w:r>
        <w:br w:type="page" />
      </w:r>
    </w:p>
    <w:altChunk r:id="AltChunkId6d862de7-c477-42bc-baa4-c42441e5b03b" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
    <w:p>
      <w:r>
        <w:br w:type="page" />
      </w:r>
    </w:p>
    <w:altChunk r:id="AltChunkIdbfd7ea64-4cd0-4acf-9d6f-f3d405c021ca" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" />
  </w:body>
</w:document>

我不确定为什么您确切地使用那些w:altChunk元素和相关部分来组合多个Word文档。这需要Microsoft Word进行“繁重的工作”,尽管在您的情况下,直接生成正确的标记可能非常容易。例如,将文档保存在Microsoft Word中后,主文档部分将如下所示(带有其他XML命名空间,为清楚起见,我将其删除):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
            xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml"
            mc:Ignorable="w14">
  <w:body>
    <w:p w14:paraId="76D6BC46" w14:textId="77777777" w:rsidR="00EA51EB" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:t>Start of template 'report-Part1.docx'</w:t>
      </w:r>
    </w:p>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="firstTag"/>
        <w:id w:val="-1950995891"/>
      </w:sdtPr>
      <w:sdtEndPr/>
      <w:sdtContent>
        <w:p w14:paraId="2701CE6E" w14:textId="77777777" w:rsidR="00EA51EB" w:rsidRDefault="00B25FEF">
          <w:r>
            <w:t>report-Part1: First value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="secondTag"/>
        <w:id w:val="551584029"/>
      </w:sdtPr>
      <w:sdtEndPr/>
      <w:sdtContent>
        <w:p w14:paraId="0B591553" w14:textId="77777777" w:rsidR="00EA51EB" w:rsidRDefault="00B25FEF">
          <w:r>
            <w:t>report-Part1: Second value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:p w14:paraId="7393CFF0" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:t>End of template 'report-Part1.docx'</w:t>
      </w:r>
    </w:p>
    <w:p w14:paraId="089D32A3" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:br w:type="page"/>
      </w:r>
    </w:p>
    <w:p w14:paraId="11AC41DA" w14:textId="77777777" w:rsidR="00716CCA" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:lastRenderedPageBreak/>
        <w:t>Start of template 'report-Part2.docx'</w:t>
      </w:r>
    </w:p>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="firstTag"/>
        <w:id w:val="-1559003811"/>
      </w:sdtPr>
      <w:sdtEndPr/>
      <w:sdtContent>
        <w:p w14:paraId="1867093C" w14:textId="77777777" w:rsidR="00716CCA" w:rsidRDefault="00B25FEF">
          <w:r>
            <w:t>report-Part2: First value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="secondTag"/>
        <w:id w:val="-1480071868"/>
      </w:sdtPr>
      <w:sdtEndPr/>
      <w:sdtContent>
        <w:p w14:paraId="43DA0FC0" w14:textId="77777777" w:rsidR="00716CCA" w:rsidRDefault="00B25FEF">
          <w:r>
            <w:t>report-Part2: Second value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:p w14:paraId="1F9B0122" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:t>End of template 'report-Part2.docx'</w:t>
      </w:r>
    </w:p>
    <w:p w14:paraId="18873AAA" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:br w:type="page"/>
      </w:r>
    </w:p>
    <w:p w14:paraId="16E23FE9" w14:textId="77777777" w:rsidR="003C3D2D" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:lastRenderedPageBreak/>
        <w:t>Start of template 'report-Part3.docx'</w:t>
      </w:r>
    </w:p>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="firstTag"/>
        <w:id w:val="780077040"/>
      </w:sdtPr>
      <w:sdtEndPr/>
      <w:sdtContent>
        <w:p w14:paraId="00BA914F" w14:textId="77777777" w:rsidR="003C3D2D" w:rsidRDefault="00B25FEF">
          <w:r>
            <w:t>report-Part3: First value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:sdt>
      <w:sdtPr>
        <w:tag w:val="secondTag"/>
        <w:id w:val="-823814304"/>
      </w:sdtPr>
      <w:sdtEndPr/>
      <w:sdtContent>
        <w:p w14:paraId="10653801" w14:textId="77777777" w:rsidR="003C3D2D" w:rsidRDefault="00B25FEF">
          <w:r>
            <w:t>report-Part3: Second value</w:t>
          </w:r>
        </w:p>
      </w:sdtContent>
    </w:sdt>
    <w:p w14:paraId="1622299A" w14:textId="77777777" w:rsidR="00E60EE9" w:rsidRDefault="00B25FEF">
      <w:r>
        <w:t>End of template 'report-Part3.docx'</w:t>
      </w:r>
    </w:p>
    <w:sectPr w:rsidR="00E60EE9">
      <w:pgSz w:w="12240" w:h="15840"/>
      <w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/>
      <w:cols w:space="708"/>
      <w:docGrid w:linePitch="360"/>
    </w:sectPr>
  </w:body>
</w:document>

Word将添加不必添加的w:sectPr元素(除非您需要特定的页面布局)。它还添加了不需要的w:lastRenderedPageBreaks。此外,添加到w:pParagraph)元素的属性和添加到w:id元素的元素(例如w:sdtEndPrw:sdt)是可选的。

本文链接:https://www.f2er.com/3050564.html

大家都在问