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
}
我们真正想要发生的是 zipFile
的 Dispose
,同时我们处理 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());
}
希望有一天能对某人有所帮助。
我有一个包含 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
}
我们真正想要发生的是 zipFile
的 Dispose
,同时我们处理 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());
}
希望有一天能对某人有所帮助。