Asp.net Exception of type 'System.OutOfMemoryException

Moti Mashiah
Moti Mashiah used Ask the Experts™
on
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.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
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 SaundersCo-Founder and Chief Architect
Top Expert 2016

Commented:
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
Try create temp file and use filestream instead of memorystream
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 Developer

Author

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

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial