Frage

I'm working on a project where I need the ability to unzip streams and byte arrays as well as zip them. I was running some unit tests that create the Zip from a stream and then unzip them and when I unzip them, the only way that DonNetZip sees them as a zip is if I run streamToZip.Seek(o,SeekOrigin.Begin) and streamToZip.Flush(). If I don't do this, I get the error "Cannot read Block, No data" on the ZipFile.Read(stream).

I was wondering if anyone could explain why that is. I've seen a few articles on using it to actually set the relative read position, but none that really explain why in this situation it is required.

Here is my Code:

Zipping the Object:

   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;
    }

Unzipping the Object:

 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 method I had to add:

    private static void FlushStream(Stream stream)
    {
        stream.Seek(0, SeekOrigin.Begin);
        stream.Flush();
    }
War es hilfreich?

Lösung

When you return output from ZipObject, that stream is at the end - you've just written the data. You need to "rewind" it so that the data can then be read. Imagine you had a video cassette, and had just recorded a program - you'd need to rewind it before you watched it, right? It's exactly the same here.

I would suggest doing this in ZipObject itself though - and I don't believe the Flush call is necessary. I'd personally use the Position property, too:

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;
}

Andere Tipps

When you write to a stream, the position is changed. If you want to decompress it (the same stream object), you'll need to reset the position. Else you'll get a EndOfStreamException because the ZipFile.Read will start at the stream.Position.

So

stream.Seek(0, SeekOrigin.Begin); 

Or

stream.Position = 0; 

would do the trick.


Offtopic but sure useful:

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;
        }
    }
}

Won't unzip all items in memory (because of the MemoryStream used in the UnZipObject(), only when iterated. Thats because extracted items are yielded. (returning an IEnumerable<Stream>) More info on yield: http://msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx

Normally i wouldn't recomment returning data as stream, because the stream is something like an iterator (using the .Position as current position). This way it isn't by default threadsafe. I'd rather return these memory streams as ToArray().

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top