61

I am trying to create a new ZIP package from code with one entry and save the ZIP package to a file. I am trying to achive this with the System.IO.Compression.ZipArchive class. I am creating the ZIP package with the following code:

using (MemoryStream zipStream = new MemoryStream())
{
    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create))
    {
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }

Then I save the ZIP to a file either in WinRT:

        var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.zip", CreationCollisionOption.ReplaceExisting);
        zipStream.Position = 0;
        using (Stream s = await file.OpenStreamForWriteAsync())
        {
            zipStream.CopyTo(s);
        }

Or in normal .NET 4.5:

        using (FileStream fs = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
        {
            zipStream.Position = 0;
            zipStream.CopyTo(fs);
        }

However, I can't open the produced files neither in Windows Explorer, WinRAR, etc. (I checked that the size of the produced file matches the Length of the zipStream, so the stream itself was saved to the file correctly.)
Am I doing something wrong or is there a problem with the ZipArchive class?

Daniel B
  • 3,109
  • 2
  • 33
  • 42
Mark Vincze
  • 7,737
  • 8
  • 42
  • 81
  • 1
    Here's a good example if you are adding binary data instead of strings: https://stackoverflow.com/questions/48927574/create-zip-file-in-memory-from-bytes-text-with-arbitrary-encoding – John Gilmer Apr 26 '20 at 18:05

6 Answers6

126

I found the—in hindsight, obvious—error in my code. The ZipArchive has to be disposed to make it write its content to its underlying stream. So I had to save the stream to a file after the end of the using block of the ZipArchive.
And it was important to set the leaveOpen argument of its constructor to true, to make it not close the underlying stream. So here is the complete working solution:

using (MemoryStream zipStream = new MemoryStream())
{
    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    {
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }
    }

    var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(
        "test.zip",
        CreationCollisionOption.ReplaceExisting);

    zipStream.Position = 0;
    using (Stream s = await file.OpenStreamForWriteAsync())
    {
        zipStream.CopyTo(s);
    }
}
Mark Vincze
  • 7,737
  • 8
  • 42
  • 81
  • I get the error, "The name 'Windows' does not exist in the current context". How can I rectify this? – Michael Murphy Apr 25 '15 at 10:17
  • 1
    Michael, this is a build error, right? Are you developing a Windows Store app, or a normal .NET app? In a normal .NET app you should use the `FileStream` class, and not the storage classes in the `Windows.Storage` namespace. You can see an example in the question. – Mark Vincze Apr 25 '15 at 20:47
  • Thanks @MarkVincze that was not entirely obvious at all to me. Saved me tearing my hair out. – The Senator Nov 23 '22 at 11:15
  • I would argue it wasn't obvious at all! It has no flush or close indicating that it does this. Relying on disposal to implement functionality is disgusting design. But thanks for the post, saved my bacon. – Adam Hardy Aug 30 '23 at 03:57
5
// Create file "archive.zip" in current directory use it as destination for ZIP archive
using (var zipArchive = new ZipArchive(File.OpenWrite("archive.zip"), 
ZipArchiveMode.Create))
{
    // Create entry inside ZIP archive with name "test.txt"
    using (var entry = zipArchive.CreateEntry("test.txt").Open())
    {
        // Copy content from current directory file "test.txt" into created ZIP entry 
        using (var file = File.OpenRead("test.txt"))
        {
            file.CopyTo(entry);
        }
    }
}    

In result you will get archive "archive.zip" with single entry file "test.txt". You need this cascade of using to avoid any interaction with already disposed resources.

Andrew Dragan
  • 71
  • 1
  • 2
4

On all of your Stream Object you must rewind the streams from the beggining in order from them to be read correctly by other applications using the .Seek method.

Example:

zipStream.Seek(0, SeekOrigin.Begin);
Freeman
  • 5,691
  • 3
  • 29
  • 41
1

You can follow the same idea, only in reverse order, using the file stream like source. I did the form below and the file was opened normally:

string fileFormat = ".zip"; // any format
string filename = "teste" + fileformat;

StorageFile zipFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename,CreationCollisionOption.ReplaceExisting);

using (Stream zipStream = await zipFile.OpenStreamForWriteAsync()){

   using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create)){
        //Include your content here
   }
}
1

In My case the Issue was that I was not disposing "zipArchive" when the zip file Task Completes. Even though I was flushing and closing all the streams.

So, Either use using as suggested in answers above

using (var zipArchive = new ZipArchive(File.OpenWrite("archive.zip"), 
ZipArchiveMode.Create))

Or Dispose the variable at the end of the Task...

zipArchive.Dispose();
Ritesh Mishra
  • 133
  • 1
  • 5
0

The complete code looks like this:

var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.zip",CreationCollisionOption.ReplaceExisting);
using (Stream zipStream = await zipFile.OpenStreamForWriteAsync())
{
    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    {
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine("Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }
    }   
}