Avatar of Moti Mashiah
Moti Mashiah
Flag for Canada asked on

Asp.net Exception of type 'System.OutOfMemoryException

Hi Guys,
I am using asp.net mvc application with VB.net and I am trying to zip files and stream them back to the user to download.

Here is what I am doing
   Public Function DownloadFiles(attachmentsSelected As String) As ActionResult
        Dim obj As FileDownloads = New FileDownloads()
        ''get file list of files to download
        Dim filesCol = obj.GetFile(attachmentsSelected).ToList()

        Using memoryStream = New MemoryStream()
            Using ziparchive = New ZipArchive(memoryStream, ZipArchiveMode.Create, True)

                For i As Integer = 0 To filesCol.Count - 1
       //get the error here   ziparchive.CreateEntryFromFile(filesCol(i).FilePath + "\" + filesCol(i).FileName, filesCol(i).FileName, CompressionLevel.Optimal)
                Next
            End Using

            Return File(memoryStream.ToArray(), "application/zip", "Attachments.zip")
        End Using

    End Function

Open in new window


The issue with this code is when I am trying to download large file ...let's say 500mb I get an exception.

"Exception of type 'System.OutOfMemoryException' was thrown."

Any idea how can I fix this issue.

Thank you.
ASP.NETVisual Basic.NET.NET Programming

Avatar of undefined
Last Comment
Moti Mashiah

8/22/2022 - Mon
Snarf0001

You're basically just eating up too much memory as the entire file contents of 500 are stored in your memory stream.
You can get around it with a bit of a rework.  Solution is to use the Response.OutputStream directly as the target of the ZipArchive.  The hiccup, is that the .net implementation "thinks" that it needs to be a seekable stream, and returns an error if you try to do that.

But it doesn't actually have to be seekable.  If you make a stream wrapper class that overrides the "CanSeek" property, and use that to pass the outputstream directly to the ziparchive you don't consume any memory, and you don't have the lag of creating the zip in the first place.

Apologies, I have the class in c# not vb, but I'm sure you can pick up the critical parts.

    public class ZipStreamWrapper : Stream
    {
        public ZipStreamWrapper(Stream stream)
        {
            baseStream = stream;
            length = 0;
        }

        private int length;
        private Stream baseStream;

        public override bool CanRead { get { return true; } }
        public override bool CanSeek { get { return false; } }
        public override bool CanWrite { get { return true; } }

        public override long Length
        {
            get { return length; }
        }

        public override long Position
        {
            get { return length; }
            set
            {
                throw new NotImplementedException();
            }
        }

        public override void Flush()
        {
            baseStream.Flush();
        }
        public override int Read(byte[] buffer, int offset, int count)
        {
            return baseStream.Read(buffer, offset, count);
        }
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }
        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }
        public override void Write(byte[] buffer, int offset, int count)
        {
            baseStream.Write(buffer, offset, count);
            length += count;
        }
    }

Open in new window


And then just create the archive with this instead:
new ZipArchive(new ZipStreamWrapper(Response.OutputStream), ZipArchiveMode.Create))

Open in new window

Dustin Saunders

You're using a contiguous region of memory to load the stream, so on large files you're pretty much always going to get that exception.  You'll want to use some sort of FileStream.

What may be helpful is to take a look at this blog post: https://blog.stephencleary.com/2016/11/streaming-zip-on-aspnet-core.html
ASKER CERTIFIED SOLUTION
Ark

THIS SOLUTION ONLY AVAILABLE TO MEMBERS.
View this solution by signing up for a free trial.
Members can start a 7-Day free trial and enjoy unlimited access to the platform.
See Pricing Options
Start Free Trial
GET A PERSONALIZED SOLUTION
Ask your own question & get feedback from real experts
Find out why thousands trust the EE community with their toughest problems.
Snarf0001

Writing directly to the output stream will be far better on resources, speed and latency than using an intermediate filestream.
Try the stream wrapper as suggested.
I started with Experts Exchange in 2004 and it's been a mainstay of my professional computing life since. It helped me launch a career as a programmer / Oracle data analyst
William Peck
Moti Mashiah

ASKER
Hi guys,
Thank you for your advises.
I ended up using filestream  which solve my problem thank you all.