Downloading files from a server to client, using ASP.Net, when file size is too big for MemoryStream using Generic Handlers (ashx)

时间:2021-02-18 21:28:33

Currently, I was trying to write an ASP.Net application that involved a user clicking a ASP.Net button control

 
 
 
 
 
 

C#

 
<asp:Button ID="btnDownload" runat="server" Text="Download File" OnClick="btnDownload_Click"/>
1
2
3
 
<asp:Button ID="btnDownload"runat="server"Text="Download File"OnClick="btnDownload_Click"/>
 

Once the control is clicked the back end stores the users information (first name, last name, email address, phone number, etc…) to a database. After that information is stored to a database, the system would then allow the user to download the file through an generic handler (.ashx file). The file size that was to be downloaded is 600MB.

Download button onclick event handler
 
 
 
 
 
 

C#

 
protected void btnDownload_Click(object sender, EventArgs e)
{
connectionString = WebConfigurationManager.ConnectionStrings["connectionstring"].ConnectionString;
SqlConnection con = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("UserDownloadedFile", con);
cmd.CommandType = CommandType.StoredProcedure;

cmd.Parameters.Add(new SqlParameter("@Guid", SqlDbType.UniqueIdentifier));
cmd.Parameters["@Guid"].Value = new Guid(ViewState["Guid"].ToString());

try
{
int rowsAffected;
rowsAffected = 0;
con.Open();

rowsAffected = cmd.ExecuteNonQuery();

if (rowsAffected > 0)
{
Response.Redirect("http://localhost/someWebApp/DownloadFile.ashx");
}
else
{
// Something went wrong.
}
}
catch (SqlException exception)
{
// Log exception
}
finally
{
con.Close();
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
protectedvoidbtnDownload_Click(objectsender,EventArgse)
    {
        connectionString=WebConfigurationManager.ConnectionStrings["connectionstring"].ConnectionString;
        SqlConnection con=newSqlConnection(connectionString);
        SqlCommand cmd=newSqlCommand("UserDownloadedFile",con);
        cmd.CommandType=CommandType.StoredProcedure;
 
        cmd.Parameters.Add(newSqlParameter("@Guid",SqlDbType.UniqueIdentifier));
        cmd.Parameters["@Guid"].Value=newGuid(ViewState["Guid"].ToString());
 
        try
        {
            introwsAffected;
            rowsAffected=0;
            con.Open();
 
            rowsAffected=cmd.ExecuteNonQuery();
 
            if(rowsAffected>0)
            {
                 Response.Redirect("http://localhost/someWebApp/DownloadFile.ashx");
            }
            else
            {
                  // Something went wrong.
            }
        }
        catch(SqlException exception)
        {
               // Log exception
        }
        finally
        {
            con.Close();
        }
    }
 

Once the database is updated, I call the DownloadFile.ashx generic handler. Upon execution of the generic handler I received the following “OutOfMemoryException” error

Downloading files from a server to client, using ASP.Net, when file size is too big for MemoryStream using Generic Handlers (ashx)

This was a result from the following generic handler I was using

Former DownloadFile generic handler
 
 
 
 
 
 

C#

 
<%@ WebHandler Language="C#" class="DownloadFile" %>

using System;
using System.IO;
using System.Web;

public class DownloadFile : IHttpHandler {

public void ProcessRequest(HttpContext context)
{
string mediaName = "myFile.zip"; // 600MB in file size

if (string.IsNullOrEmpty(mediaName))
{
return;
}

string destPath = context.Server.MapPath("~/Downloads/" + mediaName);
// Check to see if file exist
FileInfo fi = new FileInfo(destPath);

// If the file exist on the server then add it to the database
if (fi.Exists)
{
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AppendHeader("Content-Length", fi.Length.ToString());
HttpContext.Current.Response.ContentType = "application/x-zip-compressed";
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=" + mediaName);
HttpContext.Current.Response.BinaryWrite(ReadByteArryFromFile(destPath));
HttpContext.Current.Response.End();
}
}

private byte[] ReadByteArryFromFile(string destPath)
{
byte[] buff = null;
FileStream fs = new FileStream(destPath, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
long numBytes = new FileInfo(destPath).Length;
buff = br.ReadBytes((int)numBytes);
return buff;
}

public bool IsReusable
{
get
{
return false;
}
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 
<%@WebHandlerLanguage="C#"Class="DownloadFile"%>
 
usingSystem;
usingSystem.IO;
usingSystem.Web;
 
publicclassDownloadFile:IHttpHandler{
 
    publicvoidProcessRequest(HttpContext context)
    {
        stringmediaName="myFile.zip";// 600MB in file size
 
        if(string.IsNullOrEmpty(mediaName))
        {
            return;
        }
 
        stringdestPath=context.Server.MapPath("~/Downloads/"+mediaName);
        // Check to see if file exist
        FileInfo fi=newFileInfo(destPath);
 
        // If the file exist on the server then add it to the database
        if(fi.Exists)
        {
            HttpContext.Current.Response.ClearHeaders();
            HttpContext.Current.Response.ClearContent();
            HttpContext.Current.Response.AppendHeader("Content-Length",fi.Length.ToString());
            HttpContext.Current.Response.ContentType="application/x-zip-compressed";
            HttpContext.Current.Response.AppendHeader("Content-Disposition","attachment; filename="+mediaName);
            HttpContext.Current.Response.BinaryWrite(ReadByteArryFromFile(destPath));
            HttpContext.Current.Response.End();
        }
    }
 
    privatebyte[]ReadByteArryFromFile(stringdestPath)
    {
        byte[]buff=null;
        FileStream fs=newFileStream(destPath,FileMode.Open,FileAccess.Read);
        BinaryReader br=newBinaryReader(fs);
        longnumBytes=newFileInfo(destPath).Length;
        buff=br.ReadBytes((int)numBytes);
        returnbuff;
    }
 
    publicboolIsReusable
    {
        get
        {
            returnfalse;
        }
    }
 
}
 

This handler worked in the past, for very small files. The last time I was using this the file size was about 300MB. My friend @homeraguas reminded me that I might needed to check my web.config file to be sure that the following maxRequestLength and executionTimeout was set. It wasn’t in the web.config file. So I added the following

httpRuntime maxRequestLength and executionTimeout settings
 
 
 
 
 
 

C#

 
<httpRuntime maxRequestLength="600000" executionTimeout="7200"/>
1
2
3
 
<httpRuntime maxRequestLength="600000"executionTimeout="7200"/>
 

Recompiled and published, then gave it another go. Still, I came across the “OutOfMemoryException” error. I looked around the net and came across this blog’s article utilizing the following method Reponse.TransmitFile();

The HttpReponse.TransmitFile()method basically states it “Writes the specified file directly to an HTTP response output stream without buffering it in memory.”

This makes sense to me, since the file I want to transfer is 600MB and I do not think the current server I am writing this web application for does not have adequate resources available. So the revision to the code I wrote/used is as follows

Current DownloadFile.ashx generic handler
 
 
 
 
 
 

C#

 
<%@ WebHandler Language="C#" class="DownloadFile" %>

using System;
using System.IO;
using System.Web;

public class DownloadFile : IHttpHandler {

public void ProcessRequest(HttpContext context)
{
string mediaName = "myFile.zip"; // 600MB in file size
if (string.IsNullOrEmpty(mediaName))
{
return;
}

string destPath = context.Server.MapPath("~/Downloads/" + mediaName);
// Check to see if file exist
FileInfo fi = new FileInfo(destPath);

try
{
if (fi.Exists)
{
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=" + fi.Name);
HttpContext.Current.Response.AppendHeader("Content-Length", fi.Length.ToString());
HttpContext.Current.Response.ContentType = "application/octet-stream";
HttpContext.Current.Response.TransmitFile(fi.FullName);
HttpContext.Current.Response.Flush();
}
}
catch (Exception exception)
{
HttpContext.Current.Response.ContentType = "text/plain";
HttpContext.Current.Response.Write(exception.Message);
}
finally
{
HttpContext.Current.Response.End();
}
}

public bool IsReusable
{
get
{
return false;
}
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
 
<%@WebHandlerLanguage="C#"Class="DownloadFile"%>
 
usingSystem;
usingSystem.IO;
usingSystem.Web;
 
publicclassDownloadFile:IHttpHandler{
 
    publicvoidProcessRequest(HttpContext context)
    {        
        stringmediaName="myFile.zip";// 600MB in file size
        if(string.IsNullOrEmpty(mediaName))
        {
            return;
        }
 
        stringdestPath=context.Server.MapPath("~/Downloads/"+mediaName);
        // Check to see if file exist
        FileInfo fi=newFileInfo(destPath);
 
        try
        {
            if(fi.Exists)
            {
                HttpContext.Current.Response.ClearHeaders();
                HttpContext.Current.Response.ClearContent();
                HttpContext.Current.Response.AppendHeader("Content-Disposition","attachment; filename="+fi.Name);
                HttpContext.Current.Response.AppendHeader("Content-Length",fi.Length.ToString());
                HttpContext.Current.Response.ContentType="application/octet-stream";
                HttpContext.Current.Response.TransmitFile(fi.FullName);
                HttpContext.Current.Response.Flush();                
            }
        }
        catch(Exception exception)
        {
            HttpContext.Current.Response.ContentType="text/plain";
            HttpContext.Current.Response.Write(exception.Message);
        }
        finally
        {
            HttpContext.Current.Response.End();
        }
    }
 
    publicboolIsReusable
    {
        get
        {
            returnfalse;
        }
    }
 
}
 

The result was that it worked.

Downloading files from a server to client, using ASP.Net, when file size is too big for MemoryStream using Generic Handlers (ashx)

What do you guys think? Is this an adequate solution? Have a good one!