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.
LVL 1
Moti Mashiah.NET DeveloperAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Snarf0001Commented:
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 SaundersDirector of OperationsCommented:
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
ArkCommented:
Try create temp file and use filestream instead of memorystream

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Snarf0001Commented:
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.
Moti Mashiah.NET DeveloperAuthor Commented:
Hi guys,
Thank you for your advises.
I ended up using filestream  which solve my problem thank you all.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
ASP.NET

From novice to tech pro — start learning today.