简体   繁体   中英

DotNetZip creates 0kb files when passing in memory stream

I have a Razor page which I want to generate a Zip file containing multiple CSV files.

It works fine when I just want to generate one file, eg

public async Task<FileStreamResult> OnGet(int id)
{
    var bankDetails = _paymentFileGenerator.GeneratePaymentFiles(id);

    await using var memoryStream = new MemoryStream();
    await using var streamWriter = new StreamWriter(memoryStream);
    await using var csvWriter = new CsvWriter(streamWriter, CultureInfo.InvariantCulture)
    {
        Configuration = { HasHeaderRecord = false, }
    };

    csvWriter.WriteRecords(bankDetails);
    streamWriter.Flush();

    return new FileStreamResult(new MemoryStream(memoryStream.ToArray()), new MediaTypeHeaderValue("text/csv"))
    {
        FileDownloadName = "bacs.csv"
    };
}

But when I try to pass memory streams for two files into a DotNetZip stream the zip downloads to the browser but both files are 0kb. Any thoughts on why?

public async Task<FileStreamResult> OnGet(int id)
{
    var bankFiles = _paymentFileGenerator.GeneratePaymentFiles(id);

    using var zipStream = new MemoryStream();
    using var zip = new ZipFile();

    await using var bankFileStream = new MemoryStream();
    await using var bankFileStreamWriter = new StreamWriter(bankFileStream);
    await using var bankFileCsvWriter = new CsvWriter(bankFileStreamWriter, CultureInfo.InvariantCulture)
    {
        Configuration = { HasHeaderRecord = false, }
    };

    bankFileCsvWriter.WriteRecords(bankFiles.BankFile);
    bankFileCsvWriter.Flush();
    bankFileStream.Seek(0, SeekOrigin.Begin);
    zip.AddEntry("bacs.csv", (name, stream) => bankFileStream.ToArray());

    await using var internalFileStream = new MemoryStream();
    await using var internalFileStreamWriter = new StreamWriter(internalFileStream);
    await using var internalFileCsvWriter = new CsvWriter(internalFileStreamWriter, CultureInfo.InvariantCulture);

    internalFileCsvWriter.WriteRecords(bankFiles.InternalFile);
    internalFileCsvWriter.Flush();
    internalFileStream.Seek(0, SeekOrigin.Begin);
    zip.AddEntry("internal.csv", (name, stream) => internalFileStream.ToArray());

    zip.Save(zipStream);

    zipStream.Seek(0, SeekOrigin.Begin);

    return new FileStreamResult(new MemoryStream(zipStream.ToArray()), new MediaTypeHeaderValue("application/zip"))
    {
        FileDownloadName = "paymentbatch.zip"
    };
}

I've seen other StackOverflow posts where people suggested adding the Seek() function to reset the position of the streams but it didn't work for me whether that was there or not.

When debugging, I can see that the 'bankfileStream' stream has bytes in it when I call the zip.AddEntry() but then the zipStream shows 0 bytes when I call zip.Save(zipStream).

Any suggestions appreciated!

I tried many different options and nothing worked until I used the SharpZipLib library instead. Here is the full solution:

public async Task<FileStreamResult> OnGet(int id)
{
    var bankFiles = _paymentFileGenerator.GeneratePaymentFiles(id);
    var bankFileBytes = await GetCsvFileBytes(bankFiles.BankFile, includeHeader: false);
    var internalFileBytes = await GetCsvFileBytes(bankFiles.InternalFile);

    var files = new List<AttachedFile>
    {
        new AttachedFile("bacs.csv", bankFileBytes),
        new AttachedFile("internal.csv", internalFileBytes)
    };

    var zipStream = AddFilesToZip(files);

    return new FileStreamResult(zipStream, new MediaTypeHeaderValue("application/zip"))
    {
        FileDownloadName = "paymentbatch.zip"
    };
}

public MemoryStream AddFilesToZip(List<AttachedFile> attachedFiles)
{
    var outputMemStream = new MemoryStream();
    using (var zipStream = new ZipOutputStream(outputMemStream))
    {
        // 0-9, 9 being the highest level of compression
        zipStream.SetLevel(3);

        foreach (var file in attachedFiles)
        {
            var newEntry = new ZipEntry(file.Name) {DateTime = DateTime.Now};

            zipStream.PutNextEntry(newEntry);

            StreamUtils.Copy(new MemoryStream(file.Bytes), zipStream, new byte[4096]);
        }

        zipStream.CloseEntry();

        // Stop ZipStream.Dispose() from also Closing the underlying stream.
        zipStream.IsStreamOwner = false;
    }

    outputMemStream.Position = 0;
    return outputMemStream;
}

private static async Task<byte[]> GetCsvFileBytes<T>(List<T> records, bool includeHeader = true) where T : class
{
    await using var bankFileStream = new MemoryStream();
    await using var bankFileStreamWriter = new StreamWriter(bankFileStream);
    await using var bankFileCsvWriter = new CsvWriter(bankFileStreamWriter, CultureInfo.InvariantCulture)
    {
        Configuration = {HasHeaderRecord = includeHeader}
    };
    bankFileCsvWriter.WriteRecords(records);
    bankFileStreamWriter.Flush();
    return bankFileStream.ToArray();
}

public class AttachedFile
{
    public byte[] Bytes { get; set; }
    public string Name { get; set; }

    public AttachedFile(string name, byte[] bytes)
    {
        Bytes = bytes;
        Name = name;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM