我正在做一个项目,我需要能够解压缩流和字节数组以及压缩它们。我正在运行一些单元测试,这些测试从流中创建 Zip,然后解压缩它们,当我解压缩它们时,DonNetZip 将它们视为 zip 的唯一方法是运行 streamToZip.Seek(o,SeekOrigin.Begin)
并streamToZip.Flush()
。如果我不这样做,我会在ZipFile.Read(stream)
上收到错误"无法读取块,没有数据"。
我想知道是否有人可以解释为什么会这样。我已经看过一些关于使用它来实际设置相对读取位置的文章,但没有一篇真正解释为什么在这种情况下需要它。
这是我的代码:
压缩对象:
public Stream ZipObject(Stream data)
{
var output = new MemoryStream();
using (var zip = new ZipFile())
{
zip.AddEntry(Name, data);
zip.Save(output);
FlushStream(output);
ZippedItem = output;
}
return output;
}
解压缩对象:
public List<Stream> UnZipObject(Stream data)
{
***FlushStream(data); // This is what I had to add in to make it work***
using (var zip = ZipFile.Read(data))
{
foreach (var item in zip)
{
var newStream = new MemoryStream();
item.Extract(newStream);
UnZippedItems.Add(newStream);
}
}
return UnZippedItems;
}
我必须添加的冲洗方法:
private static void FlushStream(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
stream.Flush();
}
当您从 ZipObject
返回 output
时,该流位于末尾 - 您刚刚写入了数据。您需要"倒带"它,以便可以读取数据。想象一下,你有一个录像带,刚刚录制了一个节目 - 你需要在观看之前倒带它,对吧?这里完全一样。
不过,我建议在ZipObject
本身这样做 - 我不认为Flush
电话是必要的。我个人也会使用 Position
属性:
public Stream ZipObject(Stream data)
{
var output = new MemoryStream();
using (var zip = new ZipFile())
{
zip.AddEntry(Name, data);
zip.Save(output);
}
output.Position = 0;
return output;
}
写入流时,位置会更改。如果要解压缩它(同一流对象),则需要重置位置。否则你会得到一个EndOfStreamException
,因为ZipFile.Read
将从stream.Position
开始。
所以
stream.Seek(0, SeekOrigin.Begin);
或
stream.Position = 0;
会做这个伎俩。
题外话,但肯定有用:
public IEnumerable<Stream> UnZipObject(Stream data)
{
using (var zip = ZipFile.Read(data))
{
foreach (var item in zip)
{
var newStream = new MemoryStream();
item.Extract(newStream);
newStream.Position = 0;
yield return newStream;
}
}
}
不会解压缩内存中的所有项目(因为UnZipObject()
中使用的MemoryStream
,仅在迭代时解压缩。那是因为提取的物品是有收益的。(返回IEnumerable<Stream>
)有关产量的更多信息:http://msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx
通常我不会将返回的数据作为流重新注释,因为流类似于迭代器(使用 .位置作为当前位置)。这样,默认情况下它不是线程安全的。我宁愿将这些内存流作为ToArray()
返回。