Search code examples
c#.netmemorystreamsharpziplib

Archiving Multiple Files Using ZipOutputStream gives me an Empty Archived File


I am attempting to download a bunch of files that I am zipping up(archiving) via ZipoutputStream.

using (var zipStream = new ZipOutputStream(outputMemStream))
{
    foreach (var documentIdString in documentUniqueIdentifiers)
    {   
        ...
        var blockBlob = container.GetBlockBlobReference(documentId.ToString());

        var fileMemoryStream = new MemoryStream();

        blockBlob.DownloadToStream(fileMemoryStream);

        zipStream.SetLevel(3); 

        fileMemoryStream.Position = 0;

        ZipEntry newEntry = new ZipEntry(document.FileName);
        newEntry.DateTime = DateTime.Now;

        zipStream.PutNextEntry(newEntry);

        fileMemoryStream.Seek(0, SeekOrigin.Begin);
        StreamUtils.Copy(fileMemoryStream, zipStream, new byte[4096]);

        zipStream.IsStreamOwner = false; // False stops the Close also Closing the underlying stream.

    }

    outputMemStream.Seek(0, SeekOrigin.Begin);
    return outputMemStream;
}

In my controller I am returning the following code that should download the Zip file i created in the previous example. The controller actions downloads the file as it should in the browser, but the Archived File is empty. I can see the content length populated returning from the method above...

file.Seek(0, SeekOrigin.Begin);
return File(file, "application/octet-stream", "Archive.zip");

Does anyone have any idea why my file that is returned by my controller is empty or corrupt?


Solution

  • I believe you need to close your entries and your final zip stream. You should also using and dispose all of your streams. Try this:

    using (var zipStream = new ZipOutputStream(outputMemStream))
    {
        zipStream.IsStreamOwner = false;
        // Set compression level
        zipStream.SetLevel(3); 
    
        foreach (var documentIdString in documentUniqueIdentifiers)
        {   
            ...
            var blockBlob = container.GetBlockBlobReference(documentId.ToString());
    
            using (var fileMemoryStream = new MemoryStream())
            {
                // Populate stream with bytes
                blockBlob.DownloadToStream(fileMemoryStream);
    
                // Create zip entry and set date
                ZipEntry newEntry = new ZipEntry(document.FileName);
                newEntry.DateTime = DateTime.Now;
                // Put entry RECORD, not actual data
                zipStream.PutNextEntry(newEntry);
                // Copy data to zip RECORD
                StreamUtils.Copy(fileMemoryStream, zipStream, new byte[4096]);
                // Mark this RECORD closed in the zip
                zipStream.CloseEntry();
            }
        }
    
        // Close the zip stream, parent stays open due to !IsStreamOwner
        zipStream.Close();
    
        outputMemStream.Seek(0, SeekOrigin.Begin);
        return outputMemStream;
    }
    

    EDIT - you should remove:

    // Reset position of stream
    fileMemoryStream.Position = 0;
    

    Pretty sure that's the problem.