logo
Handling Custom File Downloads in ASP.NET With Server Controls
[C# -BETA 2]

By Peter A. Bromberg, Ph.D.

Peter Bromberg  

Recently on the microsoft.public.dotnet.framework.aspnet newsgroup, I responded to somebody's post about having a corrupted file when using a control to create a download function (like the right hand click) which is called when the user clicks a button. He gave some code which reads a file into a buffer and sends the buffer to the browser, but said the file which was downloaded was corrupt.

After playing around with some code, I realized that he needed to set the MIME content-type and additionally, he really wanted to "Force" a download dialog with a custom filename in response to the click event. Here is the code I posted back to the newsgroup, which solved the problem:

 

<%@ Page Language="C#" %>
<%@ Import Namespace="System.IO" %>
<HTML>
<HEAD>
<script runat="server" ID=Script1>
void Page_Load(object sender, System.EventArgs e) {
if (Page.IsPostBack){
FileStream MyFileStream = new FileStream(@"d:\inetpub\wwwroot\small.pdf", FileMode.Open);
long FileSize;
FileSize = MyFileStream.Length;
byte[] Buffer = new byte[(int)FileSize];
MyFileStream.Read(Buffer, 0, (int)MyFileStream.Length);
MyFileStream.Close();
Response.ContentType="application/pdf";
Response.AddHeader( "content-disposition","attachment; filename=MyPDF.PDF");
Response.BinaryWrite(Buffer);
}
}
</script>
</HEAD>
<body>
<form runat="server">
<asp:button id="link1" Text = "get PDF" runat="server" />
</form>
</body>
</HTML>

 

You can see that the key here is that on PostBack, we fire the FileStream code to read our file (of course your file may not be a PDF, and it most likely won't be located on your server at d:\inetpub\wwwroot\small.pdf ). After reading the desired file into the buffer, and before we Response.BinaryWrite the bffer to the client, we're setting the following required headers:

Response.ContentType="application/pdf";
Response.AddHeader( "content-disposition","attachment; filename=MyPDF.PDF");

That's all it takes to create a custom download button in C# / ASP.NET.

 

READER UPDATE:

I received the following from a reader (webmaster@lovespyt.com) after this article was originally posted. I think it contains some more valuable information on this subject, and I'm reprinting it with the author's permission:



"You recently discussed custom file downloads at http://www.eggheadcafe.com/articles/20011006.asp, and I just wanted to send you some VB.NET code I created to do the same thing, but to also extend that by adding basic support for download resume programs such as Go!Zilla and GetRight. Support is not (yet) added for segmented downloading feature in Getright, because it looks like ASP.NET is tossing up some error, but unfortunately I don't know how to analyze GetRights request string or the response of the ASP.NET page to that request. It's not full W3C standards compatible, as the range header can include multiple segments (http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.16). Also important in this resuming was to make sure that it sent a 206 message back (Partial Content) as opposed to a 200 (OK) message. I found when developing this, it was important to set both the code and the description. Anyways, this should help out developers looking to secure various files without having to map extensions to IIS and the like. I use this class to serve dynamic videos after first checking user priviledges and a download count."

Imports System
Imports System.Web
Imports System.IO

Namespace LovesPYT

Public Class FileHandling

Shared Public Sub DownloadFile(FilePath as String, Optional
ContentType as String = "")

If File.Exists(FilePath) Then
Dim myFileInfo as FileInfo
Dim StartPos as Long = 0, FileSize as Long, EndPos as Long

myFileInfo = New FileInfo(FilePath)
FileSize = myFileInfo.Length
EndPos = FileSize

HttpContext.Current.Response.Clear()
HttpContext.Current.Response.ClearHeaders()
HttpContext.Current.Response.ClearContent()

Dim Range as String =
HttpContext.Current.Request.Headers("Range")
If Not ((Range Is Nothing) or (Range = "")) Then
Dim StartEnd as Array =
Range.SubString(Range.LastIndexOf("=")+1).Split("-")
If Not StartEnd(0)="" Then
StartPos = CType(StartEnd(0), Long)
End If
If StartEnd.GetUpperBound(0) >= 1 and Not StartEnd(1)="" Then
EndPos = CType(StartEnd(1), Long)
Else
EndPos = FileSize-StartPos
End If
If EndPos > FileSize then
EndPos = FileSize - StartPos
End If
HttpContext.Current.Response.StatusCode=206
HttpContext.Current.Response.StatusDescription="Partial
Content"
HttpContext.Current.Response.AppendHeader("Content-Range",
"bytes " & StartPos &"-"& EndPos & "/" & FileSize)
End If

If Not (ContentType="") and (StartPos = 0) Then
HttpContext.Current.Response.ContentType = ContentType
End If
HttpContext.Current.Response.AppendHeader("Content-disposition",
"attachment; filename=" & myFileInfo.Name)
HttpContext.Current.Response.WriteFile(FilePath, StartPos,
EndPos)
HttpContext.Current.Response.End()
End If

End Sub
End Class
End Namespace

Peter Bromberg is an independent consultant specializing in distributed .NET solutionsa Senior Programmer /Analyst at in Orlando and a co-developer of the EggheadCafe.com developer website. He can be reached at pbromberg@yahoo.com

 
Do you have a question or comment about this article? Have a programming problem you need to solve? Post it at eggheadcafe.com forums and receive immediate email notification of responses.