Currently, I was trying to write an ASP.Net application that involved a user clicking a ASP.Net button control
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.
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
|
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
(
)
;
}
}
|
Once the database is updated, I call the DownloadFile.ashx generic handler. Upon execution of the generic handler I received the following “OutOfMemoryException” error
This was a result from the following generic handler I was using
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
|
<%
@
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
;
}
}
}
|
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
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
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
|
<%
@
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
;
}
}
}
|
The result was that it worked.
What do you guys think? Is this an adequate solution? Have a good one!