OpenXml 图像关系不存在

OpenXml image relationship doesn't exist

我是一家大公司的实习生,负责处理以前实习生的项目,该项目在某些时候显然有效,但现在它坏了。该程序的作用是从文档中取出一堆文本和图像并将它们插入到模板文档中。问题是,大约一半的图像没有形成关系,我得到了红色的 X "Image cannot be displayed" 空框。我一直在使用生产力工具进行一些挖掘,我发现有几个重复的 ID,以及很多不存在的关系,尽管查看他的代码我不确定是什么原因造成的.以下是他复制图片的两种方法:

internal static void CopyImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<Blip> sourceBlips = DocumentHelper.GetAllBlips(oldTable);
        List<Blip> targetBlips = DocumentHelper.GetAllBlips(newTable);

        foreach (Blip sourceBlip in sourceBlips)
        {
            foreach (Blip targetBlip in targetBlips)
            {
                if (targetBlip.Embed.Value == sourceBlip.Embed.Value)
                {
                    if (testData.MainDocumentPart.GetPartById(sourceBlip.Embed.Value) is ImagePart imagePart)
                    {
                        ImagePart newImagePart = testReport.MainDocumentPart.AddPart(imagePart);

                        targetBlip.Embed.Value = testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                        break;
                    }
                }
            }
        }
    }

    internal static void CopyEmbeddedVisioImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<EmbeddedObject> sourceObjects = oldTable.Descendants<EmbeddedObject>().ToList();
        List<EmbeddedObject> targetObjects = newTable.Descendants<EmbeddedObject>().ToList();

        foreach (EmbeddedObject targetobj in targetObjects)
        {
            foreach (EmbeddedObject sourceObj in sourceObjects)
            {
                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<ImageData>()
                                .FirstOrDefault().RelationshipId) is ImagePart oldImagePart)
                {
                    ImagePart newImagePart = testReport.MainDocumentPart.AddPart(oldImagePart);

                    targetobj.Descendants<ImageData>().FirstOrDefault().RelationshipId =
                    testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                }


                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<OleObject>()
                                .FirstOrDefault().Id) is OpenXmlPart openXmlPart)
                {
                    EmbeddedObjectPart newEmbeddedObj = (EmbeddedObjectPart)testReport.MainDocumentPart.AddPart(openXmlPart);

                    targetobj.Descendants<OleObject>().FirstOrDefault().Id =
                                testReport.MainDocumentPart.GetIdOfPart(newEmbeddedObj);
                }
            }
        }
    }

我试过对文档调用 Save() 和 Close()。我什至尝试调用 Dispose()。 using(WordprocessingDocument foo = WordprocessingDocument.Open(bar, false){} 似乎也没有帮助。我现在不太担心重复的 ID,但我不知道为什么只有一些关系正在形成而其他关系却没有。这是一个庞大的项目,因此浏览其中的一些内容可能会非常棘手。

编辑:可能还值得一提的是,图像在某一时刻停止形成关系。这不是随机的。大约 2/3 的下降 none 图像有效。

这是一组更新的方法

internal static void CopyImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<Blip> sourceBlips = DocumentHelper.GetAllBlips(oldTable);
        List<Blip> targetBlips = DocumentHelper.GetAllBlips(newTable);

        foreach (Blip sourceBlip in sourceBlips)
        {
            foreach (Blip targetBlip in targetBlips)
            {
                if (targetBlip.Embed.Value == sourceBlip.Embed.Value)
                {
                    if (testData.MainDocumentPart.GetPartById(sourceBlip.Embed.Value) is ImagePart imagePart)
                    {
                        //ImagePart newImagePart = testReport.MainDocumentPart.AddPart(imagePart);
                        ImagePart newImagePart = testReport.MainDocumentPart.AddImagePart(imagePart.ContentType);
                        newImagePart.FeedData(imagePart.GetStream(FileMode.Open, FileAccess.Read));
                        targetBlip.Embed.Value = testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                        break;
                    }
                }
            }
        }
    }

    internal static void CopyEmbeddedVisioImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<EmbeddedObject> sourceObjects = oldTable.Descendants<EmbeddedObject>().ToList();
        List<EmbeddedObject> targetObjects = newTable.Descendants<EmbeddedObject>().ToList();

        foreach (EmbeddedObject targetobj in targetObjects)
        {
            foreach (EmbeddedObject sourceObj in sourceObjects)
            {
                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<ImageData>()
                                .FirstOrDefault().RelationshipId) is ImagePart oldImagePart)
                {
                    //ImagePart newImagePart = testReport.MainDocumentPart.AddPart(oldImagePart);
                    ImagePart newImagePart = testReport.MainDocumentPart.AddImagePart(oldImagePart.ContentType);
                    newImagePart.FeedData(oldImagePart.GetStream(FileMode.Open, FileAccess.Read));

                    targetobj.Descendants<ImageData>().FirstOrDefault().RelationshipId =
                    testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                }


                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<OleObject>()
                                .FirstOrDefault().Id) is OpenXmlPart openXmlPart)
                {
                    EmbeddedObjectPart newEmbeddedObj = (EmbeddedObjectPart)testReport.MainDocumentPart.AddPart(openXmlPart);

                    targetobj.Descendants<OleObject>().FirstOrDefault().Id =
                                testReport.MainDocumentPart.GetIdOfPart(newEmbeddedObj);
                }
            }
        }
    }

这是我的最新发现。

无法将源文档中的图像按原样添加到目标文档中; 一幅图像在其父文档中有一个唯一的 id/number,如果已经存在具有相同 ID 的图像,则该图像可能与目标文档冲突。 替换以下行

ImagePart newImagePart = testReport.MainDocumentPart.AddPart(imagePart);

与下面的那个。这里嵌入了一个全新的图像文件并分配了一个新的 id。

ImagePart newImagePart = testReport.MainDocumentPart.AddImagePart(oldImagePart.ContentType);
newImagePart.FeedData(oldImagePart.GetStream(FileMode.Open, FileAccess.Read));

目标文档中的 ID 必须是唯一的,这一点很重要。 我分享了一些(旧的(er))代码片段,关于我如何处理将一个文档中的图像合并到另一个文档中。 (这是一个 more complete/complex 实现的片段,其中检测到重复图像并防止多次插入。)

它首先遍历源文档中的所有绘图,并构建一个列表以及它们在本源文档中的原始 ID。然后将所有图像插入到目标文档中;这样做时,目标文档中的新 ID 将映射到每个项目。

源文档中的每张图都会更新为目标文档中的 ID;该列表包含原始源 ID 和新目标 ID。 (这听起来很奇怪,但当时对我来说只有这给出了预期的结果。)

只有在图像合并完成后,内容(段落和表格)才会合并到目标文档中,其中包括添加这些项目的克隆。

public class DocumentMerger
{
    private readonly WordprocessingDocument _targetDocument;

    public DocumentMerger(WordprocessingDocument targetDocument)
    {
        this._targetDocument = targetDocument;
    }    

    public void Merge(WordprocessingDocument sourceDocument)
    {
        ImagesMerger imagesMerger = new ImagesMerger(this._targetDocument);
        this._imagesMerger.Merge(sourceDocument);

        // Merge the content; paragraphs and tables.

        this._targetDocumentPart.Document.Save();
    }    
}


public class ImageInfo
{
    private String _id;
    private ImagePart _image;
    private readonly String _originalId;

    private ImageInfo(ImagePart image, String id)
    {  
        this._id = id;
        this._image = image;
        this._originalId = id;
    }

    public String Id 
    { 
        get { return this._id; } 
    }

    public ImagePart Image
    {
        get { return this._image; }
    }

    public String OriginalId
    {
        get { return this._originalId; }
    }

    public static ImageInfo Create(MainDocumentPart documentPart, ImagePart image)
    {
        String id = documentPart.GetIdOfPart(image);
        ImageInfo r = new ImageInfo(image, id);
        return r;
    }    

    public void Reparent(MainDocumentPart documentPart)
    {   
        ImagePart newImage = documentPart.AddImagePart(this._image.ContentType);                
        newImage.FeedData(this._image.GetStream(FileMode.Open, FileAccess.Read));
        String newId = documentPart.GetIdOfPart(newImage);                        
        this._id = newId;
        this._image = newImage;                
    }    
}


public class ImagesMerger 
{
    private readonly IList<ImageInfo> _imageInfosOfTheTargetDocument = new List<ImageInfo>();        
    private readonly MainDocumentPart _targetDocumentPart;

    public ImagesMerger(WordprocessingDocument targetDocument)
    {
        this._targetDocumentPart = targetDocument.MainDocumentPart;
    }

    public void Merge(WordprocessingDocument sourceDocument)
    {
        MainDocumentPart sourceDocumentPart = sourceDocument.MainDocumentPart;
        IList<ImageInfo> imageInfosOfTheSourceDocument = this.getImageInfos(sourceDocumentPart);
        if (0 == imageInfosOfTheSourceDocument.Count) { return; }

        this.addTheImagesToTheTargetDocument(imageInfosOfTheSourceDocument);
        this.rereferenceTheImagesToTheirCorrespondingImageParts(sourceDocumentPart, imageInfosOfTheSourceDocument);
    }

    private void addTheImagesToTheTargetDocument(IList<ImageInfo> imageInfosOfTheSourceDocument)
    {
        for (Int32 i = 0, j = imageInfosOfTheSourceDocument.Count; i < j; i++)
        {
            imageInfoOfTheSourceDocument.Reparent(this._targetDocumentPart);
            this._imageInfosOfTheTargetDocument.Add(imageInfoOfTheSourceDocument);                    
        }            
    }

    private IList<ImageInfo> getImageInfos(MainDocumentPart documentPart)
    {
        List<ImageInfo> r = new List<ImageInfo>();

        foreach (ImagePart image in documentPart.ImageParts)
        {
            ImageInfo imageInfo = ImageInfo.Create(documentPart, image);
            r.Add(imageInfo);
        }

        return r;
    }

    private void rereferenceTheImagesToTheirCorrespondingImageParts(MainDocumentPart sourceDocumentPart, IList<ImageInfo> imageInfosOfTheSourceDocument)
    {
        IEnumerable<Drawing> images = sourceDocumentPart.Document.Body.Descendants<Drawing>();

        foreach (Drawing image in images)
        {
            Blip blip = image.Inline.Graphic.GraphicData.Descendants<Blip>().FirstOrDefault();
            String originalId = blip.Embed.Value;

            ImageInfo imageInfo = imageInfosOfTheSourceDocument.FirstOrDefault(o => o.OriginalId._Equals(originalId));
            blip.Embed.Value = imageInfo.Id;
        }
    }
}