0

I have this Azure function, that should return a zip file.

On returning the FileStreamResult i get

System.Private.CoreLib: Cannot access a closed Stream.

But my return statement is within the using of the memoryStream.

Any help is very appreciated.

BR Kresten

namespace returnZipfile
{
    public static class ReturnZipfile
    {
        [FunctionName("ReturnZipfile")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            using (var memoryStream = new MemoryStream())
            {
                using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
                {
                    ZipArchiveEntry archiveFile = archive.CreateEntry("Manifest.xml");
                    using (var entryStream = archiveFile.Open())
                    {
                        using (var streamWriter = new StreamWriter(entryStream))
                        {
                            streamWriter.Write("Manifest content");
                        }
                    }

                    archiveFile = archive.CreateEntry("PackageHeader.xml");
                    using (var entryStream = archiveFile.Open())
                    {
                        using (var streamWriter = new StreamWriter(entryStream))
                        {
                            streamWriter.Write("PackageHeader content");
                        }
                    }

                    archiveFile = archive.CreateEntry("VendorInvoiceHeaders.csv");
                    using (var entryStream = archiveFile.Open())
                    {
                        using (var streamWriter = new StreamWriter(entryStream))
                        {
                            streamWriter.Write("Header content");
                        }
                    }
                }
                return new FileStreamResult(memoryStream, "application/octet-stream")
                {
                    FileDownloadName = "datapackage.zip"
                };
            }
        }
    }
}
Kresten
  • 810
  • 13
  • 36
  • keep ```memoryStream.Position = 0;``` before return in your code. Does this help? – RithwikBojja Mar 21 '23 at 09:50
  • Can it be the ZipArchive closes the stream? Try moving your return statement into the second using block (or use using without braces). Also when you write into the stream the stream pointer might be at the end of the stream. So before you return say `memoryStream.Position = 0`. – Maxim Zabolotskikh Mar 21 '23 at 09:51
  • 1
    You need to remove the `using()` block over your `memoryStream` because that means the `MemoryStream` will be closed when your `Run` action method returns - which happens _before_ the `FileStreamResult` is evaluated. – Dai Mar 21 '23 at 10:09
  • `"application/octet-stream"` <-- This is **not** the correct MIME Content-Type for a zip file. [Use `application/zip` instead](https://www.iana.org/assignments/media-types/application/zip). – Dai Mar 21 '23 at 10:10

1 Answers1

2
  • You're using MemoryStream incorrectly:

    • You need to remove the using() block over your memoryStream because that means the MemoryStream will be closed (via the implicit call to memoryStream.Dispose() when your Run action method returns - which happens before the FileStreamResult is actually executed by the parent webserver.

    • Additionally, you need to "rewind" the MemoryStream before returning it by setting .Position = 0.

    • It's also important that you use the leaveOpen: true parameter on ZipArchive's constructor. Your current code does-so already; though I recommend using named-parameters to make your code more self-documenting.

    • And don't forget that you can have multiple using() statements sharing the same indent level.

  • Also, you're using the wrong MIME Content-Type for PKZip (*.zip) files: it's application/zip and not application/octet-stream, as per the IANA: https://www.iana.org/assignments/media-types/application/zip

  • I've refactored your code to move the ZipArchiveEntry-consumers into a separate method:

namespace returnZipfile
{
    public static class ReturnZipfile
    {
        [FunctionName("ReturnZipfile")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log
        )
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

            MemoryStream ms = new MemoryStream();
            using( ZipArchive archive = new ZipArchive( ms, ZipArchiveMode.Create, leaveOpen: true ) )
            {
                PopulateZipArchive( archive );
            }

            ms.Position = 0;

            return new FileStreamResult(ms, "application/zip")
            {
                FileDownloadName = "datapackage.zip"
            };
        }

        private static void PopulateZipArchive( ZipArchive archive )
        {
            ZipArchiveEntry manifestXmlEntry = archive.CreateEntry("Manifest.xml");
            using (var entryStream = manifestXmlEntry.Open())
            using (var streamWriter = new StreamWriter(entryStream))
            {
                streamWriter.Write("Manifest content");
            }

            ZipArchiveEntry packageHeaderXmlEntry = archive.CreateEntry("PackageHeader.xml");
            using (var entryStream = packageHeaderXmlEntry.Open())
            using (var streamWriter = new StreamWriter(entryStream))
            {
                streamWriter.Write("PackageHeader content");
            }

            ZipArchiveEntry headersCsvEntry = archive.CreateEntry("VendorInvoiceHeaders.csv");
            using (var entryStream = headersCsvEntry.Open())
            using (var streamWriter = new StreamWriter(entryStream))
            {
                streamWriter.Write("Header content");
            }
        }
    }
}
Dai
  • 141,631
  • 28
  • 261
  • 374