我正在开发一个项目,我需要能够解压缩流和字节数组以及压缩它们。我正在运行一些单元测试,从流中创建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;
}
Flush方法我必须添加:
private static void FlushStream(Stream stream)
{
stream.Seek(0, SeekOrigin.Begin);
stream.Flush();
}
答案 0 :(得分:5)
当您从output
返回ZipObject
时,该流位于 end - 您刚刚编写了数据。您需要“回放”它,以便可以读取数据。想象一下,你有一个录像带,刚刚录制了一个程序 - 你需要在观看之前回放它,对吗?这里完全一样。
我建议在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;
}
答案 1 :(得分:3)
写入流时,位置会发生变化。如果要解压缩它(相同的流对象),则需要重置位置。否则,您将获得EndOfStreamException
,因为ZipFile.Read
将从stream.Position
开始。
所以
stream.Seek(0, SeekOrigin.Begin);
或者
stream.Position = 0;
会做到这一点。
Offtopic但确实有用:
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;
}
}
}
不会解压缩内存中的所有项目(因为MemoryStream
中使用的UnZipObject()
,只有在迭代时才会解压缩。这就是因为提取的项目已经产生了。(返回IEnumerable<Stream>
)更多收益率信息:http://msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx
通常我不建议将数据作为流返回,因为流类似于迭代器(使用.Position作为当前位置)。这种方式不是默认的线程安全。我宁愿将这些内存流作为ToArray()
返回。