|
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
|