Downloading a file using Content-Disposition: attachment as HTTP response header displays a "File Download" dialog box asking the user whether to "Open", "Save" or "Cancel" the document. In certain scenarios this dialog box can be really painful specially when user has to repeatedly do this exercise for multiple links available on our website. In order to avoid this we use Content-Disposition: inline which opens the document without asking by using appropriate software like MS Word, Adobe Acrobat Reader etc. installed on the user's machine. This document can be saved later on using "Save As" option.
Before I move on, let's have a look on the following statement that shows the way to add Content-Disposition header to Response stream:
context.Response.AddHeader("Content-Disposition", "inline; filename=resume.doc")
The problem with the Content-Disposition: inline is that it doesn't pick the file name from filename attribute we use in AddHeader function rather, it always takes the name of web page from the URL while saving the document. Content-Disposition: attachment seems to be working fine even with the "Save As" command, but this is something I didn't want to use. I spent some time researching on this issue and couldn't found a workable solution. In order to workaround I came up with the below solution that seems to be doing the job.
I actually decided to use a URL for the document link that reflects the name of the document being downloaded. For example for the document "MyResume.doc", the URL I have used looks something like this:
<a href="Default/FileName/882bd3e9-5035-4946-8c2a-98ef0eccc6e0/uploads/MyResume.doc.aspx">Download File</a>
Did you notice ".aspx" at the end of the URL? It is very important to append it in the end in order to send the request back to server whenever user clicks on the link. On the server, I have created an HTTPHandler to catch this sort of request that contains "*.doc.aspx". Without appending ".aspx", browser will server your document directly without changing the URL in the browser. But, we want to change the URL which means request has to go to the server.
The rest of the solution is very easy as I have written below code under the body of ProcessRequest function in my HTTPHanlder which helps dump file contents into response stream. It is also important to note that URL of the document must be relative and not absolute. The reason being, it's a dummy URL which doesn't exist at all. Relative URL guarantees that request will come to the same web site/application where we can catch it before it goes to ASP.NET engine. QueryString data can be made part of the URL as in my case GUID is the piece of information which is part of the URL and I receive it on the server.
Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) Implements System.Web.IHttpHandler.ProcessRequest
context.Response.ContentType = "application/msword"
context.Response.AddHeader("Content-Disposition", "inline; filename=adeel.doc")
The last bit is to add the below statement in web.config to enable HTTPHandler to catch "*.*.aspx" so that it will serve for all kinds of file type extensions.
<add verb="*" path="/uploads/*.*.aspx" type="Test.HttpHandler, Test"/>