XmlDocument 无法从 ZipArchive 条目加载流

XmlDocument fails to load stream from ZipArchive entry

我有一个包含 XML 文件的 zip 文件。使用 System.IO.Compression ZipArchive 我打开 XML 文件作为流(实际上是 DeflateStream):

Stream xmlStream = null;
using (ZipArchive archive = ZipFile.Open(destPath, ZipArchiveMode.Read))
{
    xmlStream = archive.GetEntry(fileNameXml).Open();
}

现在我可以使用 Stream.Read 方法从流中读取:

// This works
byte[] buffer = new byte[10];
int tmp = xmlStream.Read(buffer, 0, 10);

然而,我真正想做的是像这样将数据加载到 XmlDocument 中:

XmlDocument xmlDoc = new XmlDocument();
// This throws 'NotSupportedException'
xmlDoc.Load(xmlStream);

但这会引发 NotSupportedException 消息 "This stream from ZipArchiveEntry does not support reading."

我当然可以提取 XML 文件并打开文件,但直接从流中读取在我看来是一个更好的解决方案 - 如果我能让它工作的话!

好的 - 找到了我可以接受的解决方法。

我不知道 XML 文档是如何尝试读取 DeflateStream 的,因为我尝试从流中手动读取的所有方法都有效。

我使用此解决方法将数据保存在流中并将该流加载到我的 XmlDocument 中:

MemoryStream readerStream = new MemoryStream();
xmlStream.CopyTo(readerStream);
readerStream.Position = 0;
xmlDoc.Load(readerStream);

如果有人对 DeflateStream 表现出的奇怪行为有任何解释,或者有更好的方法将数据从 DeflateStream 获取到 xml 文档中,我很想听

可能是因为它被包装在一个 using 中,所以当你真正从流中读取它时它已经被处理掉了。

当你有这样的方法时会发生这种情况:

    public Stream OpenStream(string filePath)
    {
        using(var zipFile = ZipFile.OpenRead(filePath))
        {
            var entry = zipFile.GetEntry("entryName");
            return entry.Open();
        }
    }

并像这样使用它:

    using(var stream = OpenStream(filePath))        
    {
        // NotSupportedException: This stream from ZipArchiveEntry does not support reading.
        // because zipFile is already disposed before reading the entry stream
    }

我们真正想要发生的是 zipFileDispose,同时我们处理 entry.Open() 返回的 Stream。有几种方法可以解决这个问题,根据您的设计,它可能很简单。其中一种方法是围绕流创建包装器 class 并同时处理两个流。

public class OnDisposeStream : Stream
{
    private readonly Stream _stream;
    private readonly Action _onDispose;

    public OnDisposeStream(Stream stream, Action onDispose)
    {
        _stream = stream;
        _onDispose = onDispose;
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        _onDispose();
    }

    public override void Flush()
    {
        _stream.Flush();
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        return _stream.Seek(offset, origin);
    }

    public override void SetLength(long value)
    {
        _stream.SetLength(value);
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        return _stream.Read(buffer, offset, count);
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        _stream.Write(buffer, offset, count);
    }

    public override bool CanRead => _stream.CanRead;
    public override bool CanSeek => _stream.CanSeek;
    public override bool CanWrite => _stream.CanWrite;
    public override long Length => _stream.Length;

    public override long Position
    {
        get { return _stream.Position; }
        set { _stream.Position = value; }
    }
}

然后把我们原来的方法改成这样:

    public Stream OpenStream(string filePath)
    {
        var zipFile = ZipFile.OpenRead(filePath);
        var entry = zipFile.GetEntry("entryName");
        return new OnDisposeStream(entry.Open(), () => zipFile.Dispose());
    }        

希望有一天能对某人有所帮助。