XmlReader - 如何在没有 System.OutOfMemoryException 的情况下读取元素中很长的字符串

Posted

技术标签:

【中文标题】XmlReader - 如何在没有 System.OutOfMemoryException 的情况下读取元素中很长的字符串【英文标题】:XmlReader - How to read very long string in element without System.OutOfMemoryException 【发布时间】:2019-06-05 04:55:12 【问题描述】:

我必须读取从 API 返回的 XML 元素中的文件内容 Base64 字符串。

我的问题是这个字符串可能很长,具体取决于文件大小。

起初,我使用XmlDocument 来读取XML。现在我使用XmlReader 来避免 System.OutOfMemoryException 当 XML 太大时。

但是当我阅读字符串时,我也收到了System.OutOfMemoryException。 我猜这个字符串太长了。

using (XmlReader reader = Response.ResponseXmlXmlReader)

    bool found = false;
    //Read result
    while (reader.Read() && !found)
    
        if(reader.NodeType == XmlNodeType.Element && reader.Name == "content")
        
            //Read file content
            string file_content = reader.ReadElementContentAsString();
            //Write file
            File.WriteAllBytes(savepath + file.name, Convert.FromBase64String(file_content));

            //Set Found!
            found = true;
        
    
 

如何在没有System.OutOfMemoryException 的情况下读取带有XmlReader 的文件内容字符串?

【问题讨论】:

您可能可以使用XmlReader.ReadValueChunk 逐个读取和解码大型 Base64 内容。确保 char 缓冲区的大小允许整个缓冲区完全进行 Base64 解码。由于 Base64 字符始终编码 6 位,因此选择一个缓冲区大小 numB64Chars 解码为 numBytes 字节其中 numB64Chars = numBytes * 4/3 (= numBytes * 8/6) (旁注:注意文档。XmlReader.ReadValueChunk 不保证它会在一次调用中填充缓冲区。而是检查 XmlReader.ReadValueChunk 的返回值以查看它有多少 Base64 字符已读取,如有必要再次调用此方法 - 当然,使用适当调整的参数 - 直到缓冲区完全填满或到达内容末尾) @elgonzo 谢谢。这是一个很好的解决方案。我只搜索了 readelement.... 下次,我必须阅读更多文档。 【参考方案1】:

您可以为此目的使用XmlReader.ReadElementContentAsBase64(Byte[] buffer, Int32 index, Int32 count)。此方法允许以块的形式读取和解码 XML 元素的 Base64 元素内容,从而避免大型元素的 OutOfMemoryException

例如,您可以引入以下扩展方法:

public static class XmlReaderExtensions

    public static bool ReadToAndCopyBase64ElementContentsToFile(this XmlReader reader, string localName, string namespaceURI, string path)
    
        if (!reader.ReadToFollowing(localName, namespaceURI))
            return false;
        return reader.CopyBase64ElementContentsToFile(path);
    

    public static bool CopyBase64ElementContentsToFile(this XmlReader reader, string path)
    
        using (var stream = File.Create(path))
        
            byte[] buffer = new byte[8192];
            int readBytes = 0;

            while ((readBytes = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length)) > 0)
            
                stream.Write(buffer, 0, readBytes);
            
        
        return true;
    

然后做:

var path = Path.Combine(savepath, file.name);
var found = reader.ReadToAndCopyBase64ElementContentsToFile("content", "", path);

演示小提琴here.

【讨论】:

感谢您非常明确的回答。您的 XmlReaderExtensions 课程对我很有帮助。

以上是关于XmlReader - 如何在没有 System.OutOfMemoryException 的情况下读取元素中很长的字符串的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PowerShell 中使用 XmlReader 流式传输大/巨大的 XML 文件?

使用 xmlReader 在 C# 中过滤特定元素值的大型 XML

如果没有空格分隔符,为啥 XmlReader 会跳过所有其他元素?

有没有办法让 XmlReader 将字符引用保留为文本而不是转换它?

如何在 .Net 2.0/C# 中将 StreamReader 转换为 XMLReader 对象

如何在 XMLReader 中获取标记名称的值 + 如何使用该值进行进一步分析