在 ASP.NET Core Web API 中使用 Open XML 合并两个 word 文档 - 图片丢失

Combination of two word documents using Open XML in ASP.NET Core Web API - Images are missing

我的方法很简单。我从 Internet 获取两个文件(作为 .docx 文件),获取这两个文件的 byte[]。并对目标文件执行 Append() 操作,附加源文件的克隆 Body 。下面是我的代码

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Linq;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml;
using System.Collections.Generic;

namespace WhatApp.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class DocController : ControllerBase
    {
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            byte[] file1 = await GetBytes("https://dummyfileserver.io/file/1");
            byte[] file2 = await GetBytes("https://dummyfileserver.io/file/2");

            byte[] result = MergeFiles(file1, file2);

            // To return the file
            return File(result, "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        }

        private async Task<byte[]> GetBytes(string url)
        {
            using HttpClient httpClient = new HttpClient();
            var res = await httpClient.GetAsync(url);
            if (res.IsSuccessStatusCode)
            {
                using var filestream = await res.Content.ReadAsStreamAsync();
                var filebytes = new byte[filestream.Length];
                filestream.Read(filebytes, 0, filebytes.Length);

                return filebytes;
            }
            throw new Exception();
        }

        private byte[] MergeFiles(byte[] dest, byte[] src)
        {
            using (MemoryStream destMem = new MemoryStream())
            {
                destMem.Write(dest, 0, (int)dest.Length);
                using (WordprocessingDocument mywDoc =
                    WordprocessingDocument.Open(destMem, true))
                {
                    mywDoc.MainDocumentPart.Document.Body.InsertAt(new PageBreakBefore(), 0);

                    mywDoc.MainDocumentPart.Document.Body.Append(new Paragraph(new Run(new Break() { Type = BreakValues.Page })));

                    var srcElements = GetSourceDoc(src);
                    mywDoc.MainDocumentPart.Document.Body.Append(srcElements);
                    mywDoc.Close();
                }
                return destMem.ToArray();
            }
        }

        private OpenXmlElement GetSourceDoc(byte[] src)
        {
            using (MemoryStream srcMem = new MemoryStream())
            {
                srcMem.Write(src, 0, (int)src.Length);
                using (WordprocessingDocument srcDoc =
                    WordprocessingDocument.Open(srcMem, true))
                {
                    OpenXmlElement elem = srcDoc.MainDocumentPart.Document.Body.CloneNode(true);
                    srcDoc.Close();
                    return elem;
                }
            }
        }
    }
}

结果文件未在添加 file2 的区域正确显示图像(响应文档的第二部分)。

这个问题一定是什么原因造成的?如何解决?

我注意到的另一个问题是在我将文件保存到本地计算机后调试强制停止。一定是什么原因造成的?

图像是单独存储的,您也需要手动包含它们。您还需要修复 OpenXml 中的所有关系。不幸的是,OpenXML 并不简单,SDK 也没有隐藏这种复杂性。

但是,如果您知道您的 word 文档是由理解 AltChunks 的软件(即 MS Word)打开的,那么可能有一个简单的方法供您使用: 我建议你看看 Merge multiple word documents into one Open Xml

根据我的经验:它的效果在很大程度上取决于文档的复杂性和预期用途。用 MS Word 打开它通常没问题,但例如在服务器上(使用第 3 方库)将其转换为 PDF 可能无法给出预期的结果。

我看到您要求使用 ASP.NET 核心合并两个 word 文件。我高度怀疑 AltChunks 是个好主意,因为您的响应是 FileContentResult 来自 byte[] 数组。 Indeen OpenXML 并没有隐藏复杂性。但是 OpenXML PowerTools is what I will recommend to consider. It is now maintained by Eric White and has a nuget package 也适用于 .NET 标准。只需继续安装软件包并修改您的 MergeFiles() 方法,如下所示:

private byte[] MergeFiles(byte[] dest, byte[] src)
{
    var sources = new List<Source>();
    
    var destMem  = new MemoryStream();
    destMem .Write(dest, 0, dest.Length);
    sources.Add(new Source(new WmlDocument(destMem .Length.ToString(), destMem), true));

    var srcMem  = new MemoryStream();
    srcMem .Write(src, 0, src.Length);
    sources.Add(new Source(new WmlDocument(srcMem .Length.ToString(), srcMem ), true));

    var mergedDoc = DocumentBuilder.BuildDocument(sources);

    MemoryStream mergedFileStream = new MemoryStream();
    mergedDoc.WriteByteArray(mergedFileStream);

    return mergedFileStream.ToArray();
}

Source DocumentBuilderWmlDocument 来自 OpenXmlPowerTools 命名空间。祝你好运!