【问题标题】:C# .NET Why Stream.Seek is required when unzipping streamC# .NET 为什么解压流时需要 Stream.Seek
【发布时间】:2013-09-26 12:22:30
【问题描述】:

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

【问题讨论】:

  • 在哪里,用什么Stream,你打电话给UnZipObject?而且,你对Stream之前调用UnZipObject做了什么?
  • ZippedItem = output; 是做什么的?

标签: c# .net zip unzip dotnetzip


【解决方案1】:

当您从ZipObject 返回output 时,该流位于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;
}

【讨论】:

  • 感谢现实世界的类比...正是我所需要的!
【解决方案2】:

当您写入流时,位置会改变。如果要解压缩它(相同的流对象),则需要重置位置。否则,您将获得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&lt;Stream&gt;)有关产量的更多信息:@987654321 @

通常我不建议将数据作为流返回,因为流类似于迭代器(使用 .Position 作为当前位置)。这样,默认情况下它不是线程安全的。我宁愿将这些内存流返回为ToArray()

【讨论】:

  • 感谢您的建议,我听说过几次最好传递字节数组而不是流,但我不确定我是否理解您的回答为什么?我可以举一个你提议的例子吗?
  • 一个数组可以被多个线程并发读取,一个流不能,因为多个线程会改变Position属性。字节数组也将是原始流的“副本”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-20
  • 2010-10-18
  • 1970-01-01
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多