Getting Out of Memory Exception when downloading large files - asp.net

I developed ASP.NET web application. I am trying to download the document from file server. FileSize maybe more than 1GB.
Here the thing is, very first time file got downloaded. when i click the same link, i am getting "Out of memory Exception" throws error.
If it is small size file, i don't get any issues to download the filles...
How do we handle this error if i download the document more than 1GB...?
below is the code to download the file in ASP.NET (Code behind file)
Dim binFile As Byte() = File.ReadAllBytes(filename)
Response.ContentType = "application/octet-stream"
Response.AppendHeader("Content-Disposition", "attachment; filename=" + filename)
Response.BinaryWrite(binFile)
Response.End()

Response.BinaryWrite() buffers whole file into memory, you can Use HttpResponse.TransmitFile() which don't buffer the file in memory.
Better solution is to send file as chunks, stream and ...
Here is a sample for download file without buffer
using (FileStream fileStream = File.OpenRead("full path to file"))
{
int bufferSize = 10 * 1024;
byte[] buffer = new byte[bufferSize];
long dataToRead = fileStream.Length;
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition", "attachment; filename=FILE_NAME_THAT_USER_MUST_SEE");
Response.AddHeader("Content-Length", fileStream.Length.ToString());
while (dataToRead > 0)
if (Response.IsClientConnected)
{
int readLength = fileStream.Read(buffer, 0, bufferSize);
Response.OutputStream.Write(buffer, 0, readLength);
Response.Flush();
buffer = new byte[bufferSize - 1 + 1];
dataToRead -= readLength;
}
else
break;
}
Response.Close();

Related

Returning a downloadable file using a stream in asp.net web forms

In asp.net MVC I can do something like the following which will open a stream:
Stream strm1 = GenerateReport(Id);
return File(strm1,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"Report_" + reportId.ToString() + ".xlsx");
Notice how I am passing strm1 which is a stream. I can then name it Report_+ ...xlsx like the example above shows.
Is there a similar way to do this with asp.net web forms using c#.
You can use TransmitFile or WriteFile if the file is in your website folder.
string fileName = string.Format("Report_{0}.xlsx", reportId);
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition",
string.Format("attachment; filename={0}", fileName));
Response.TransmitFile(fileName);
Response.End();
Stream
If your data is already in Memory, you want this method which writes the response in chunks.
Stream stm1 = GenerateReport(Id);
Int16 bufferSize = 1024;
byte[] buffer = new byte[bufferSize + 1];
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition",
string.Format("attachment; filename=\"Report_{0}.xlsx\";", reportId));
Response.BufferOutput = false;
int count = stm1.Read(buffer, 0, bufferSize);
while (count > 0)
{
Response.OutputStream.Write(buffer, 0, count);
count = stm1.Read(buffer, 0, bufferSize);
}
I use this extension to send a stream as a downloadable file:
public static class ToDownloadExtention
{
public static void ToDownload(this Stream stream, string fileName, HttpResponse response)
{
response.Clear();
response.ContentType = "application/octet-stream";
response.AddHeader("Content-Disposition", string.Format("attachment; filename={0}", fileName));
stream.CopyTo(response.OutputStream);
response.End();
}
}
And the usage is:
var stream = new MemoryStream();
stream.ToDownload("someFileName.ext",Response);
Or if you have a stream ready to be written, simply copy it to response stream:
Response.Clear();
Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.AddHeader("Content-Disposition", "attachment; filename={your file name}");
Response.OutputStream.Write(stream, 0, stream.length);
Response.End();
Added same code just for visibility

ServletOutputStream closes prematurely

I wrote a servlet to download a file. it works well for some files, but for others the os closes prematurely, and I end up downloading a blank file.
File file = new File(fileName);
InputStream fis = new FileInputStream(file);
resp.setHeader("Content-Type", "text/plain");
resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName );
resp.setDateHeader("Expires",0);
resp.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
resp.setHeader("Pragma", "public");
resp.setContentLength((int) file.length());
resp.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
ServletOutputStream os = resp.getOutputStream();
byte[] bufferData = new byte[1024];
int read = 0;
while((read = fis.read(bufferData))!= -1){
System.out.println("read " + read + " chars");
os.write(bufferData, 0, read);
}
os.flush();
os.close();
fis.close();
when I debug, I can see it trying to write the first buffer worth of data into os, then on the browser side, the download dialog pops up with a blank file. in the servlet, I get a IOClosed on the next buffer write.
I tried using IOUtils.copy(fis, resp.getOutputStream());
get the same error.
any reason certain type of file data can force os to close prematurely? can I protect against it?

ASP.NET - response.outputstream.write either writes 16k and then all 0's, or writes all but insetrs a char every 64k

I have the following code...
public partial class DownloadFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string FilePath = "[FTPPath]";
Download downloadFile = new Download();
Server.ScriptTimeout = 54000;
try
{
long size = downloadFile.GetFileSize(FilePath);
using (FtpWebResponse ftpResponse = downloadFile.BrowserDownload(FilePath))
using (Stream streamResponse = ftpResponse.GetResponseStream())
{
string fileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1);
int bufferSize = 65536;
byte[] buffer = new byte[bufferSize];
int readCount;
readCount = streamResponse.Read(buffer, 0, bufferSize);
// Read file into buffer
//streamResponse.Read(buffer, 0, (int)size);
Response.Clear();
Response.Buffer = false;
Response.BufferOutput = false;
//Apparently this line helps with old version of IE that like to cache stuff no matter how much you tell them!
Response.AddHeader("Pragma", "public");
//Expires: 0 forces the browser to always thing the page is "stale" therefore forcing it to never cache the page and therefore always re-downloads the page when viewed. Therefore no nasty experiences if we change the authentication details.
Response.Expires = 0;
//Again this line forces the browser not to cache the page.
Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
Response.AddHeader("Cache-Control", "public");
Response.AddHeader("Content-Description", "File Transfer");
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.AddHeader("Content-Transfer-Encoding", "binary");
Response.AddHeader("Content-Length", size.ToString());
// writes buffer to OutputStream
while (readCount > 0)
{
Response.OutputStream.Write(buffer, 0, bufferSize);
readCount = streamResponse.Read(buffer, 0, bufferSize);
Response.Flush();
}
Response.End();
Server.ScriptTimeout = 90;
}
}
catch (Exception ex)
{
Response.Write("<p>" + ex.Message + "</p>");
Server.ScriptTimeout = 90;
}
}
}
To download .zip files from an FTP (please ignore the header rubbish about preventing caching unless this is related to the issue).
So downloadFile is a class I have written using FTPWebRequest/Response with SSL enabled that can do to two things; one is return the file size (GetFileSize) of a file on our FTP and the other is to set FtpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile to allow the download of a file.
Now the code appears to work perfectly, you get a nice zip downloaded of exactly the same size as the one on the FTP however, this is where the quirks begin.
The zip files are always corrupted, no matter how small. In theory, very small files should be okay, but you'll see why in a moment. Because of this, I decided to compare the files in binary.
If I set bufferSize to anything other than the size of the file
(i.e. 1024, 2048, 65536), the first 16k (16384 bytes) downloads
perfectly, and then the stream just writes zeros to the end of the
file.
If I set bufferSize = size (filesize), the stream appears to download the full file, until you look more closely. The file is an exact replica up to the first 64k, and then an extra character appears in the downloaded file (this chararacter never seems to be the same).
After this extra byte, the files are exactly the same again. An extra byte appears to get added every 64k, meaning that by the end of 65MB file, the two files are massively out of sync. Because the download length is limited to the size of the file on the server, the end of the file gets truncated in the downloaded file. The archive will allow access to it as all the CRC checks fail.
Any help would be much appreciated. Cheers.
Now changed my code somewhat to use WebRequest and WebResponse to grabe a zip using Http from the web server itself. Here is the code...
public partial class DownloadFile : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string FilePath = [http path];
Server.ScriptTimeout = 54000;
try
{
WebRequest HWR = WebRequest.Create(FilePath);
HWR.Method = WebRequestMethods.File.DownloadFile;
using (WebResponse FWR = HWR.GetResponse())
using (BinaryReader streamResponse = new BinaryReader(FWR.GetResponseStream()))
{
string fileName = FilePath.Substring(FilePath.LastIndexOf("/") + 1);
int bufferSize = 2048;
byte[] buffer = new byte[bufferSize];
int readCount;
readCount = streamResponse.Read(buffer, 0, bufferSize);
Response.Clear();
Response.Buffer = false;
Response.BufferOutput = false;
//Apparently this line helps with old version of IE that like to cache stuff no matter how much you tell them!
Response.AddHeader("Pragma", "public");
//Expires: 0 forces the browser to always thing the page is "stale" therefore forcing it to never cache the page and therefore always re-downloads the page when viewed. Therefore no nasty experiences if we change the authentication details.
Response.Expires = 0;
//Again this line forces the browser not to cache the page.
Response.AddHeader("Cache-Control", "no-cache, must-revalidate");
Response.AddHeader("Cache-Control", "public");
Response.AddHeader("Content-Description", "File Transfer");
Response.ContentType = "application/zip";
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
Response.AddHeader("Content-Transfer-Encoding", "binary");
// writes buffer to OutputStream
while (readCount > 0)
{
Response.OutputStream.Write(buffer, 0, bufferSize);
Response.Flush();
readCount = streamResponse.Read(buffer, 0, bufferSize);
}
//Response.Write(testString);
Response.End();
Server.ScriptTimeout = 90;
}
}
catch (Exception ex)
{
Response.Write("<p>" + ex.Message + "</p>");
Server.ScriptTimeout = 90;
}
}
}
This code is more simple but it is still corrupting the data. I'm sure there's something very simple I'm doing wrong, but I just can't spot it or find a test to show me where I am going wrong. Please help :)
On your line
Response.OutputStream.Write(buffer, 0, bufferSize);
change bufferSize to readCount so that you only write the number that you actually read.

Remote file Download via ASP.NET corrupted file

I am using the code below which I have found on one of the forums to download a file in remote server. it seems it is working. However, the downloaded file is corrupted and I cannot unzip.
Do you have any idea why it is so? or if my approach is wrong, could you suggest me a better way please?
protected void Page_Load(object sender, EventArgs e)
{
string url = "http://server/scripts/isynch.dll?panel=AttachmentDownload&NoteSystem=SyncNotes&NoteType=Ticket&NoteId=1&Field=supp&File=DisplayList%2etxt";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Credentials = new NetworkCredential("user", "pass");
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
////Initialize the output stream
Response.Clear();
Response.ContentType = "application/octet-stream";
Response.AppendHeader("Content-Disposition:", "attachment; filename=" + "DisplayList.txt");
Response.AppendHeader("Content-Length", resp.ContentLength.ToString());
////Populate the output stream
byte[] ByteBuffer = new byte[resp.ContentLength];
Stream rs = req.GetResponse().GetResponseStream();
rs.Read(ByteBuffer, 0, ByteBuffer.Length);
Response.BinaryWrite(ByteBuffer);
Response.Flush();
///Cleanup
Response.End();
rs.Dispose();
}
First of all, use application/octet-stream as it is the standard content type for downloads.
new byte[resp.ContentLength + 1] will define a buffer which is one byte larger than content type. I believe this is the reason for corruption. Use new byte[resp.ContentLength].
I actually recommend re-writing it and removing memorystream:
const int BufferLength = 4096;
byte[] byteBuffer = new byte[BufferLength];
Stream rs = req.GetResponse().GetResponseStream();
int len = 0;
while ( (len = rs.Read(byteBuffer,0,byteBuffer.Length))>0)
{
if (len < BufferLength)
{
Response.BinaryWrite(byteBuffer.Take(len).ToArray());
}
else
{
Response.BinaryWrite(byteBuffer);
}
Response.Flush();
}
the article on http://support.microsoft.com/default.aspx?scid=kb;en-us;812406 solved my problem. Many thanks to #Aliostad for his effort to help me.

Error: while trying to OPen PDF in ASP.NET

In my ASP.NET application,When I try to open PDF file by using the below code, I am getting an error
CODE USED TO SHOW PDF FILE
FileStream MyFileStream = new FileStream(filePath, FileMode.Open);
long FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize + 1];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename="+filePath);
Response.BinaryWrite(Buffer);
ERROR I AMN GETTING
"There was an error opening this document.The file is damaged and could not open"
Sounds like your using an aspx file to output the pdf. Have you considered using an ashx file which is an HttpHandler? It bypasses all the typical aspx overhead stuff and is more efficient for just serving up raw data.
Here is an example of the ashx using your code:
<% WebHandler Language="c#" class="ViewPDF" %>
public class ViewPDF : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
FileStream MyFileStream = new FileStream(filePath, FileMode.Open);
long FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize + 1];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename="+filePath);
Response.BinaryWrite(Buffer);
}
public bool IsReusable
{
get { return false; }
}
}
If you still want to use the aspx page. Make sure you are doing the following:
// At the beginning before you do any response stuff do:
Response.Clear();
// When you are done all your response stuff do:
Response.End();
That should solve your problem.
You must flush the response otherwise it gets partially transmitted.
Response.Flush();
In addition to ocedcio's reply, you need to be aware that Stream.Read() does not necessarily read all of the bytes requested. You should examine the return value from Stream.Read() and continue reading if less bytes are read than requested.
See this question & answer for the details: Creating a byte array from a stream

Resources