Search code examples
c#csvzip

Compress and zip file from string


I have a big csv that I need to send by email, the file must be compressed otherwise it might hit the max limit of the smtp server. I'm generating the csv in memory based on a database query.

I was trying to use the answer on this question C# Compress and zip csv from stream. but it didn't work(possibly because of the different c# version, I'm using .net core 2.2)

The final csv is a string which I then convert to a Stream and pass it to the extensions of the answer the question.

The code I'm using from the answer(slight adapted):

    {
        public string FileName { get; set; }
        public Stream FileStream { get; set; }
    }

    public static class ZipArchiveExtensions
    {

        public static Stream GenerateStreamFromString(string s)
        {
            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            writer.Write(s);
            writer.Flush();
            stream.Position = 0;
            return stream;
        }

        public static Stream Compress(this FileModel file) => Compress(new FileModel[] { file });
        public static Stream Compress(this IEnumerable<FileModel> files)
        {
            if (files.Any())
            {
                var ms = new MemoryStream();
                var archive = new ZipArchive(ms, ZipArchiveMode.Create, false);
                foreach (var file in files)
                {
                    var entry = archive.add(file);
                }
                ms.Position = 0;
                return ms;
            }
            return null;
        }

        private static ZipArchiveEntry add(this ZipArchive archive, FileModel file)
        {
            var entry = archive.CreateEntry(file.FileName, CompressionLevel.Fastest);
            using (var stream = entry.Open())
            {
                file.FileStream.CopyTo(stream);
                // stream.Position = 0;
                stream.Close();
            }
            return entry;
        }
    }

How I'm using the code:

                var stream = ZipArchiveExtensions.GenerateStreamFromString(csvStr);
                var file = new FileModel { FileName = reportName + ".csv", FileStream = stream };
                var compressedFile = file.Compress();

                var attachment = new Attachment(compressedFile, reportName + ".zip");
                await this._emailService.SendMessageAsync(reportRequest.MailTo, reportName, null, new[] { attachment });

For some reason the result zip file on the email is invalid.


Solution

  • The ZipArchive object has to be disposed to properly write its content to its underlying stream. Additionally, you have to set the leaveOpen flag to true so upon disposing the archive, the compressedFile will not be closed.

    public static Stream Compress(this IEnumerable<FileModel> files)
    {
        if (files.Any())
        {
            var ms = new MemoryStream();
            using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
            {
                foreach (var file in files)
                {
                    var entry = archive.add(file);
                }
            }        
            ms.Position = 0;
            return ms;
        }
        return null;
    }