Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open XML SDK System.IO.IOException: "Entries cannot be opened multiple times in Update mode" during disposal #1801

Open
Yaroslav-Andrieiev-Net opened this issue Oct 3, 2024 · 0 comments
Assignees

Comments

@Yaroslav-Andrieiev-Net
Copy link

Describe the bug

Hi everyone, we faced an issue after "DocumentFormat.OpenXml" migration from 2.20.0 to latest 3.1.0 (issue reproduces in versions 3.0.0 - 3.1.0).
It happens on disposal of WordprocessingDocument
Stacktrace:
- System.IO.IOException: Entries cannot be opened multiple times in Update mode. at System.IO.Compression.ZipArchiveEntry.OpenInUpdateMode() at System.IO.Packaging.ZipStreamManager.Open(ZipArchiveEntry zipArchiveEntry, FileAccess streamFileAccess) at System.IO.Packaging.ZipPackagePart.GetStreamCore(FileMode streamFileMode, FileAccess streamFileAccess) at DocumentFormat.OpenXml.Packaging.OpenXmlPart.LoadDomTree[T]() at DocumentFormat.OpenXml.Packaging.MainDocumentPart.get_PartRootElement() at DocumentFormat.OpenXml.Packaging.OpenXmlPackage.SavePartContents(Boolean save) at DocumentFormat.OpenXml.Packaging.OpenXmlPackage.Dispose(Boolean disposing) at DocumentFormat.OpenXml.Packaging.OpenXmlPackage.Dispose() at Edocs.Infrastructure.Services.Conversion.OpenXMLCorrector.FixSharePointFile(MemoryStream stream, IFileLoggerInfo fileLoggerInfo, String extension)

Issue reproduces only with particular .docx file: File.docx

Code

public void FixSharePointFile(MemoryStream stream, IFileLoggerInfo fileLoggerInfo, string extension)
{
    WordprocessingDocument wordDoc = null;
    try
    {
        try
        {
            wordDoc = WordprocessingDocument.Open(stream, true);
        }
        catch (InvalidOperationException e)
        {
            Log.Error($"OpenXML file open exception. CaseFileId: {fileLoggerInfo.FileId}, file name: {fileLoggerInfo.FileName}", e);
            throw new BusinessException(LocalizedString.Get(Messages.OpenXMLFileOpenError));
        }

        var partsAndBindings = wordDoc.MainDocumentPart.GetXMLPartsAndBindings();
        var doc = wordDoc.MainDocumentPart.GetXmlDocByNamespace(EdocsConversionConstants.Myns);

        if (doc is { ChildNodes.Count: > 0 })
        {
            var namespaceManager = new XmlNamespaceManager(doc.NameTable);
            var xDoc = doc.ToXDocument();

            foreach (var part in partsAndBindings)
            {

                foreach (var binding in part.Item2)
                {
                    var xpath = binding.Attribute(EdocsConversionConstants.W + "xpath")?.Value;

                    var prefix = binding.Attribute(EdocsConversionConstants.W + "prefixMappings")?.Value;
                    this.PrefixResolver(prefix, namespaceManager);

                    if (xpath.IsNotEmpty())
                    {
                        var sdtPrCollection = binding.Parent?.Parent?.Descendants(EdocsConversionConstants.W + "sdtPr");
                        var rCollection = binding.Parent?.Parent?.Descendants(EdocsConversionConstants.W + "r");

                        if (sdtPrCollection.Any() && rCollection.Any())
                        {
                            var r = rCollection?.GetFirstAndRemoveOthers();

                            var sdtPr = sdtPrCollection?.First();

                            var rprCollection = sdtPr?.Descendants(EdocsConversionConstants.W + "rPr");

                            if (rprCollection.Any())
                            {
                                var rPr = rprCollection?.First();
                                var rPrRCollection = r?.Descendants(EdocsConversionConstants.W + "rPr");
                                if (rPrRCollection.Any())
                                {
                                    rPrRCollection.First()?.ReplaceWith(rPr);
                                }
                            }

                            var t = r?.Descendants(EdocsConversionConstants.W + "t")?.GetFirstAndRemoveOthers();

                            r?.Descendants(EdocsConversionConstants.W + "tab")?.GetFirstAndRemoveOthers()?.Remove();
                            r?.Descendants(EdocsConversionConstants.W + "br")?.GetFirstAndRemoveOthers()?.Remove();

                            if (t != null)
                            {
                                var v = xDoc?.XPathSelectElement(xpath, namespaceManager)?.Value;
                                if (v.IsEmpty())
                                {
                                    t.Remove();
                                }
                            }
                        }
                    }
                }

                part.Item1.PutXDocument();
            }
        }

        wordDoc?.Dispose(); // exception throwed here!

        stream?.Seek(0, SeekOrigin.Begin);
    }
    finally
    {
        wordDoc?.Dispose();
    }
}

Extenstion methods:

public static List<Tuple<OpenXmlPart, IEnumerable<XElement>>> GetXMLPartsAndBindings(this MainDocumentPart mainPart)
{
    var partsAndBindings = new List<Tuple<OpenXmlPart, IEnumerable<XElement>>>();

    foreach (var header in mainPart.HeaderParts)
    {
        var headerDoc = header.GetXDocument();

        var headerBinding = headerDoc?.Descendants(w + "dataBinding");
        if (headerBinding != null)
        {
            partsAndBindings.Add(new Tuple<OpenXmlPart, IEnumerable<XElement>>(header, headerBinding));
        }
    }

    foreach (var footer in mainPart.FooterParts)
    {
        var footerDoc = footer.GetXDocument();

        var footerBinding = footerDoc?.Descendants(w + "dataBinding");
        if (footerBinding != null)
        {
            partsAndBindings.Add(new Tuple<OpenXmlPart, IEnumerable<XElement>>(footer, footerBinding));
        }
    }
    var mainXml = mainPart.GetXDocument();
    var bindings = mainXml?.Descendants(w + "dataBinding");

    if (bindings != null)
    {
        partsAndBindings.Add(new Tuple<OpenXmlPart, IEnumerable<XElement>>(mainPart, bindings));
    }

    return partsAndBindings;
}
public static XmlDocument GetXmlDocByNamespace(this MainDocumentPart mainPart, string nameSpace)
{
    var doc = new XmlDocument();

    foreach (var part in mainPart.CustomXmlParts)
    {
        using (var stream = part.GetStream())
        {
            if (stream.Length == 0)
            {
                continue;
            }

            doc.Load(stream);

            if (doc?.DocumentElement?.NamespaceURI == nameSpace)
            {
                return doc;
            }
        }
    }

    return null;
}
public static XDocument ToXDocument(this XmlDocument document)
 {
     return document.ToXDocument(LoadOptions.None);
 }

 public static XDocument ToXDocument(this XmlDocument document, LoadOptions options)
 {
     using (var reader = new XmlNodeReader(document))
     {
         return XDocument.Load(reader, options);
     }
 }
 public static void PutXDocument(this OpenXmlPart part)
 {
     var xdoc = part.GetXDocument();
     xdoc.PutXDocument(part);
 }

 public static void PutXDocument(this XDocument xdoc, OpenXmlPart part)
 {
     if (xdoc != null)
     {
         using (var xw =
             XmlWriter.Create(part.GetStream
                 (FileMode.Create, FileAccess.Write)))
         {
             xdoc.Save(xw);
         }
     }
 }

Thanks in advance for any assistance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants