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 · 1 comment
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!

@mkaszewiak
Copy link
Collaborator

mkaszewiak commented Jan 31, 2025

Hi @Yaroslav-Andrieiev-Net
The error you are seeing occurs when code tries to open stream when another stream is open. I recommend using using statement whenever possible to avoid handling all the stream. See this issue[(https://github.com//issues/909))]

To be able to troubleshoot your issue could you please provide the following so I can test it on my site and check for errors:
Whole class including extension method as there are variables in your code like:

this.PrefixResolver(prefix, namespaceManager);

that I am not able to resolve. Also please hardcode all the constants or provide constants class

EdocsConversionConstant

Possibly issue maybe somewhere in the extension methods as you are opening streams there but without working example I am not able to tell for sure.

Thanks

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

3 participants