32

I'm trying to create a zip stream on the fly with some byte array data and make it download via my MVC action.

But the downloaded file always gives the following corrupted error when opened in windows.

enter image description here

And this error when I try to xtract from 7z

enter image description here

But note that the files extracted from the 7z is not corrupted.

I'm using ZipArchive and the below is my code.

    private byte[] GetZippedPods(IEnumerable<POD> pods, long consignmentID)
    {
        using (var zipStream = new MemoryStream())
        {
            //Create an archive and store the stream in memory.                
            using (var zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
            {
                int index = 1;
                foreach (var pod in pods)
                {                        
                    var zipEntry = zipArchive.CreateEntry($"POD{consignmentID}{index++}.png", CompressionLevel.NoCompression);                       
                    using (var originalFileStream = new MemoryStream(pod.ByteData))
                    {
                        using (var zipEntryStream = zipEntry.Open())
                        {
                            originalFileStream.CopyTo(zipEntryStream);
                        }
                    }
                }
                return zipStream.ToArray();
            }
        }
    }

    public ActionResult DownloadPOD(long consignmentID)
    {
        var pods = _consignmentService.GetPODs(consignmentID);
        var fileBytes = GetZippedPods(pods, consignmentID);
        return File(fileBytes, MediaTypeNames.Application.Octet, $"PODS{consignmentID}.zip");
    }

What am I doing wrong here.

Any help would be highly appreciated as I'm struggling with this for a whole day.

Thanks in advance

Faraj Farook
  • 13,513
  • 14
  • 70
  • 93

5 Answers5

46

Move zipStream.ToArray() outside of the zipArchive using.

The reason for your problem is that the stream is buffered. There's a few ways to deal wtih it:

  • You can set the stream's AutoFlush property to true.
  • You can manually call .Flush() on the stream.

Or, since it's MemoryStream and you're using .ToArray(), you can simply allow the stream to be Closed/Disposed first (which we've done by moving it outside the using).

DiplomacyNotWar
  • 31,605
  • 6
  • 57
  • 75
  • Would you mind explaining why buffering is an issue? – General Grievance Dec 07 '21 at 15:04
  • 1
    @CalculusWhiz imagine that `ZipArchive` writes in 4 kilobyte chunks. You write 19 kilobytes to it. It has flushed 16 bytes of data and is waiting for that 1 extra byte to flush the last chunk to the underlying stream. If you try and access the full contents of the underlying stream before this flush occurs, you will only get 16 kilobytes of data, not the full 19. You can force a flush using one of the methods I suggested above. – DiplomacyNotWar Dec 07 '21 at 15:12
9

I Dispose ZipArchive And error solved

 public static byte[] GetZipFile(Dictionary<string, List<FileInformation>> allFileInformations)
    {

        MemoryStream compressedFileStream = new MemoryStream();
        //Create an archive and store the stream in memory.
        using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create, true))
        {
            foreach (var fInformation in allFileInformations)
            {
                var files = allFileInformations.Where(x => x.Key == fInformation.Key).SelectMany(x => x.Value).ToList();
                for (var i = 0; i < files.Count; i++)
                {
                    ZipArchiveEntry zipEntry = zipArchive.CreateEntry(fInformation.Key + "/" + files[i].FileName);

                    var caseAttachmentModel = Encoding.UTF8.GetBytes(files[i].Content);

                    //Get the stream of the attachment
                    using (var originalFileStream = new MemoryStream(caseAttachmentModel))
                    using (var zipEntryStream = zipEntry.Open())
                    {
                        //Copy the attachment stream to the zip entry stream
                        originalFileStream.CopyTo(zipEntryStream);
                    }
                }
            }
            //i added this line 
            zipArchive.Dispose();

            return compressedFileStream.ToArray();
        }
    }

public void SaveZipFile(){
        var zipFileArray = Global.GetZipFile(allFileInformations);
        var zipFile = new MemoryStream(zipFileArray);
        FileStream fs = new FileStream(path + "\\111.zip", 
        FileMode.Create,FileAccess.Write);
        zipFile.CopyTo(fs);
        zipFile.Flush();
        fs.Close();
        zipFile.Close();
}
Afshin Razaghi
  • 370
  • 3
  • 8
  • Same for me: Using "using" strictly, doing all the flushes and closes and the archive keeps being corrupted. I added this one magic line, and it works perfectly. I am wondering now why this is. – Xan-Kun Clark-Davis Apr 09 '22 at 20:46
0

I was also having problems with this and I found my issue was not the generation of the archive itself but rather how I was handing my GET request in AngularJS.

This post helped me: how to download a zip file using angular

The key was adding responseType: 'arraybuffer' to my $http call.

factory.serverConfigExportZIP = function () {
    return $http({
        url: dataServiceBase + 'serverConfigExport',
        method: "GET",
        responseType: 'arraybuffer'
    })
};
Ian
  • 161
  • 1
  • 16
0

you can remove "using" and use Dispose and Close methods it's work for me

...
zip.Dispose();
zipStream.Close();
return zipStream.ToArray();
Mamad4D
  • 1
  • 1
0

I know this is a C# question but for managed C++, delete the ZipArchive^ after you're done with it to fix the error.

ZipArchive^ zar = ZipFile::Open(starget, ZipArchiveMode::Create);
ZipFileExtensions::CreateEntryFromFile(zar, sfile1, "file.txt");
ZipFileExtensions::CreateEntryFromFile(zar, sfile2, "file2.txt");
delete zar;
Adam Bruss
  • 1,624
  • 14
  • 18