here's what you're doing wrong: You have multiple pending calls to ZipEntry.OpenReader() in a single ZipFile instance. You can have at most, only one pending ZipEntry.OpenReader().
Here's why: There is only one Stream object created when you instantiate a given zip file with ZipFile.Read() or new ZipFile(), passing the name of an existing file. When you call ZipEntry.OpenReader() , it results in a Seek() in the Stream object, to move the file pointer to the beginning of the compressed bytestream for that particular entry. When you call ZipEntry.OpenReader() again, it results in another Seek() to a different location in the stream. So by adding entries and calling OpenReader() in succession, you are calling Seek() repeatedly, but only the last one will be valid. The stream cursor will be placed at the start of the data for the entry corresponding to the last call to ZipEntry.OpenReader().
To fix it: Scrap your approach. The simplest way to create a new zipfile with fewer entries than an existing zip file is this: instantiate a ZipFile by reading the existing file, then remove the entries you don't want, then call ZipFile.Save() to a new path.
using (var zip = ZipFile.Read("c:\\dir\\path\\to\\existing\\zipfile.zip"))
{
foreach (var name in namesToRemove) // IEnumerable<String>
{
zip[name].Remove();
}
zip.Save("c:\\path\\to\\new\\Archive.zip");
}
EDIT
What this does at the time you call Save(): the library reads the raw, compressed data for the entries you have NOT removed from the filesystem file, and writes them into a new archive file. This is really fast because it does not decompress and recompress each entry in order to put it into the new, smaller zip file. Basically it reads slices of binary data out of the original zip file, and concatenates them together to form the new, smaller zip file.
To produce multiple smaller files, you can do this repeatedly with the original zip file; just wrap the above in a loop and vary the files you remove, and the filename of the new, smaller archive. Reading an existing zipfile is also pretty fast.
As an alternative, you could decompress and extract each entry, and then recompress and write the entry into a new zip file. That is the long way around, but it is possible. In that case, for each smaller zipfile you want to create, you will need to create two ZipFile instances. Open the first one by reading the original zip archive. For each entry you want to keep, create a MemoryStream, extract content from an entry into that MemoryStream, and remember to call Seek() in the mem stream to reset the cursor on the memory stream. Then using the second ZipFile instance, call AddEntry(), using that MemoryStream as the source for the added entry. Call ZipFile.Save() only on the second instance.
using (var orig = ZipFile.Read("C:\\whatever\\OriginalArchive.zip"))
{
using (var smaller = new ZipFile())
{
foreach (var name in entriesToKeep)
{
var ms = new MemoryStream();
orig[name].Extract(ms); // extract into stream
ms.Seek(0,SeekOrigin.Begin);
smaller.AddEntry(name,ms);
}
smaller.Save("C:\\location\\of\\SmallerZip.zip");
}
}
This works, but it involves decompression and recompression of each entry that goes into the smaller zip, which is inefficient and unnecessary.
If you don't mind the inefficiency of the decompression and recompression, there's an alternative you can employ: call the ZipFile.AddEntry() overload that accepts opener and closer delegates. What this does is defer the call to OpenReader() til the time the entry is being written to the new, smaller zip file. The effect is that you have just one pending OpenReader() at a time.
using(ZipFile original = ZipFile.Read("C:\\path.to\\original\\Archive.zip"),
smaller = new ZipFile())
{
foreach (var name in entriesToKeep)
{
zip.AddEntry(zipEntryName,
(name) => original[name].OpenReader(),
null);
}
smaller.Save("C:\\path.to\\smaller\\Archive.zip");
}
It's still inefficient, because each entry gets decompressed and recompressed, but it's a little less inefficient.