Invalid zip file after creating it with System.IO.Compression
Asked Answered
B

6

25

I'm trying to create a zip file that contains one or more files.
I'm using the .NET framework 4.5 and more specifically System.IO.Compression namespace.
The objective is to allow a user to download a zip file through a ASP.NET MVC application.
The zip file is being generated and sent to the client but when I try to open it by doing double click on it I get the following error:
Windows cannot open the folder. The compressed (zipped) folder ... is invalid.
Here's my code:

[HttpGet]
public FileResult Download()
{
    var fileOne = CreateFile(VegieType.POTATO);
    var fileTwo = CreateFile(VegieType.ONION);
    var fileThree = CreateFile(VegieType.CARROT);

    IEnumerable<FileContentResult> files = new List<FileContentResult>() { fileOne, fileTwo, fileThree };
    var zip = CreateZip(files);

    return zip;
}

private FileContentResult CreateFile(VegieType vType)
{
    string fileName = string.Empty;
    string fileContent = string.Empty;

    switch (vType)
    {
        case VegieType.BATATA:
            fileName = "batata.csv";
            fileContent = "THIS,IS,A,POTATO";
            break;
        case VegieType.CEBOLA:
            fileName = "cebola.csv";
            fileContent = "THIS,IS,AN,ONION";
            break;
        case VegieType.CENOURA:
            fileName = "cenoura.csv";
            fileContent = "THIS,IS,A,CARROT";
            break;
        default:
            break;
    }

    var fileBytes = Encoding.GetEncoding(1252).GetBytes(fileContent);
    return File(fileBytes, MediaTypeNames.Application.Octet, fileName);
}

private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
    byte[] retVal = null;

    if (files.Any())
    {
        using (MemoryStream zipStream = new MemoryStream())
        {
            using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
            {
                foreach (var f in files)
                {
                    var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
                    using (var entryStream = entry.Open())
                    {
                        entryStream.Write(f.FileContents, 0, f.FileContents.Length);
                        entryStream.Close();
                    }
                }

                zipStream.Position = 0;
                retVal = zipStream.ToArray();
            }
        }
    }

    return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
}

Can anyone please shed some light on why is windows saying that my zip file is invalid when I double click on it.
A final consideration, I can open it using 7-Zip.

Buchenwald answered 21/10, 2016 at 11:21 Comment(0)
Q
38

You need to get the MemoryStream buffer via ToArray after the ZipArchive object gets disposed. Otherwise you end up with corrupted archive.

And please note that I have changed the parameters of ZipArchive constructor to keep it open when adding entries.

There is some checksumming going on when the ZipArchive is beeing disposed so if you read the MemoryStream before, it is still incomplete.

    private FileResult CreateZip(IEnumerable<FileContentResult> files)
    {
        byte[] retVal = null;

        if (files.Any())
        {
            using (MemoryStream zipStream = new MemoryStream())
            {
                using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
                {
                    foreach (var f in files)
                    {
                        var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
                        using (BinaryWriter writer = new BinaryWriter(entry.Open()))
                        {                                   
                            writer.Write(f.FileContents, 0, f.FileContents.Length);
                            writer.Close();
                        }
                    }

                    zipStream.Position = 0;
                }
                retVal = zipStream.ToArray();
            }
        }

        return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
    }
Quinate answered 21/10, 2016 at 13:14 Comment(5)
OP said he able to open it using 7-Zip. So Dispose should not be issueGangboard
Wondering about setting the position to 0 of the memory stream before disposing of the ZipArchive. The Dispose of the ZipArchive I believe does some finalizing of the zip.Benil
Thanks a lot Michal, you were right. It's working now.Wainscot
It would appear in my testing that I didn't need to create a MemoryStream buffer (which was good because I was creating a 14GB zip file backup), I only needed to call the Dispose method ZipArchive explicitly. This is on VS2017 Community .NET (4.6) Console project.Humdrum
@GauravKP Indeed the OP said he could open it via 7Z, but not via Windows explorer. This is the solution. Solved my problem too, which was essencially the same, only I have created a Helper class, so the ZipArchive is not in the same function as the return of the zip fileCureall
B
3

Just return the stream...

private ActionResult CreateZip(IEnumerable files)
{
    if (files.Any())
    {
        MemoryStream zipStream = new MemoryStream();
        using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))
        {
            foreach (var f in files)
            {
               var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
               using (var entryStream = entry.Open())
               {
                   entryStream.Write(f.FileContents, 0, f.FileContents.Length);
                   entryStream.Close();
               }
           }

        }

        zipStream.Position = 0;
        return File(zipStream, MediaTypeNames.Application.Zip, "horta.zip");
    }

    return new EmptyResult();
}
Benil answered 21/10, 2016 at 13:25 Comment(0)
D
1

Try changing

using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, false))

to

using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))

In this usage, the archive is forced to write to the stream when it is closed. However, if the leaveOpen argument of the constructor is set to false, it will close the underlying stream too.

Deutzia answered 21/2, 2018 at 13:11 Comment(0)
N
1

Try to add the dispose method before you return the stream to release the resources used by the current instance of the System.IO.Compression.ZipArchive class.

 private FileResult CreateZip(IEnumerable<FileContentResult> files)
{
    byte[] retVal = null;

    if (files.Any())
    {
        using (MemoryStream zipStream = new MemoryStream())
        {
            using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
            {
                foreach (var f in files)
                {
                    var entry = archive.CreateEntry(f.FileDownloadName, CompressionLevel.Fastest);
                    using (BinaryWriter writer = new BinaryWriter(entry.Open()))
                    {                                   
                        writer.Write(f.FileContents, 0, f.FileContents.Length);
                        writer.Close();
                    }
                }

                zipStream.Position = 0;
            }

            archive.Dispose();
            retVal = zipStream.ToArray();
        }
    }

    return File(retVal, MediaTypeNames.Application.Zip, "horta.zip");
Ninetta answered 30/11, 2023 at 13:19 Comment(1)
I don't quite get why/how this works but it was the solution to my issue.Kraus
R
0

When I added a wrong name for the entry as in the example

var fileToZip = "/abc.txt";
ZipArchiveEntry zipFileEntry = zipArchive.CreateEntry(fileToZip);

I got the same error. After correcting the file name, it is ok now.

Revulsion answered 8/4, 2019 at 10:55 Comment(0)
S
0

I got the "The compressed (zipped) folder ... is invalid." error because my entries were named with a leading "/" in front of them. Some zip extractors had no problem with this but the Windows one does. I resolved it by removing the slash from the entry name (from "/file.txt" to "file.txt").

Subaudition answered 9/2, 2023 at 18:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.