C#.NET为什么在解压缩流时需要Stream.Seek

时间:2013-09-26 12:22:30

标签: c# .net zip unzip dotnetzip

我正在开发一个项目,我需要能够解压缩流和字节数组以及压缩它们。我正在运行一些单元测试,从流中创建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();
    }

2 个答案:

答案 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()返回。