Home > Enterprise >  Stream zip file content to the user from website
Stream zip file content to the user from website

Time:10-29

I'm trying to serve a file to the users that is packed inside a zip archive on the server. The project is ASP.NET Core 5.0 MVC project.

I managed to do it by using ZipArchiveEntry.Open() and copying that to a memory stream.

string zipFile = @"D:\all_installs.zip";

using (FileStream fs = new FileStream(zipFile, FileMode.Open))
{
    using (ZipArchive zip = new ZipArchive(fs))
    {
        ZipArchiveEntry entry = zip.Entries.FirstOrDefault(x => x.FullName == "downloadable file.iso");

        string name = entry.FullName;
        string baseName = Path.GetFileName(name);

        //open a stream to the zip entry
        Stream stream = entry.Open();

        //copy stream to memory
        MemoryStream memoryStream = new MemoryStream();
        stream.CopyTo(memoryStream); //big memory usage?
        memoryStream.Position = 0;

        return this.File(memoryStream, "application/octet-stream", baseName);
    }
}

This would require a lot of RAM if there are many simultaneous downloads, so instead I would like to serve it directly from the archive, which I know will require the CPU while unpacking it, but that's fine since the download speed will be very limited anyways.

I tried serving stream directly, but I get the following error:

NotSupportedException: Stream does not support reading.

How can I serve the entry-stream directly?

CodePudding user response:

Problem is both FileStream fs and ZipArchive zip are disposed here, so when it's time to write response and asp.net tries to read your zip entry (stream) - it's not available any more, since everything has been disposed.

You need to not dispose them right away but instead tell asp.net to dispose them when it's done writing the response. For that, HttpResponse has method RegisterForDispose, so you need to do something like that:

string zipFile = @"C:\tmp\record.zip";

FileStream fs = null;
ZipArchive zip = null;
Stream stream = null;
try {
    fs = new FileStream(zipFile, FileMode.Open);
    zip = new ZipArchive(fs);
    ZipArchiveEntry entry = zip.Entries.First(x => x.FullName == "24fa535b-2fc9-4ce5-96f4-2ff1ef0d9b64.json");
    string name = entry.FullName;
    string baseName = Path.GetFileName(name);

    //open a stream to the zip entry
    stream = entry.Open();
    return this.File(stream, "application/octet-stream", baseName);
}
finally {
    if (stream != null)
        this.Response.RegisterForDispose(stream);
    if (zip != null)
        this.Response.RegisterForDispose(zip);
    if (fs != null)
        this.Response.RegisterForDispose(fs);
}

Now asp.net will first write the response, then dispose all your disposables for you.

  • Related