Providing a File for Download trough a Save As Dialog in ASP.NET

The question of providing a file trough a SaveAs dialog in a web browser appears very often in the forums and QA websites like Stackoverflow.

The default action of the web browser when a file is provided is to open it.
It automatically determines the file type and opens it inside the browser. An example would be a link to a .pdf, an excel or image file etc. When you click on it it opens the file in another tab or another page.

You have two options to achieve this depending whether you are using an already existing file or you are dynamically generating it(or getting it from a database or such).

The first alternative is to use HttpResponse.TransmitFile method to send the file to a response. Which will display the SaveAs dialog asking you for a location where to store
the file. First you need to set the ContentType property and add the Content-Disposition and/or Content-Length headers for the response.

Here’s an example:

string filePath = "~/path/SomeExcelFile.xlsx";
System.IO.FileInfo fileInfo = new System.IO.FileInfo(filePath);
Response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", filePath));
Response.AddHeader("Content-Lenght", fileInfo.Length.ToString());
Response.ContentType = "application/ms-excel";

This will cause a SaveAs dialog to appear with the SomeExcelFile.xlsx as the default filename.

The transmit file approach is valid only if you are providing an existing file. If for example you are generating a file dynamically or reading it from a database you can use the
property to write directly to the output stream. For example:

Bitmap bmp = new Bitmap(10, 10);
Response.ContentType = "image/jpeg";
Response.AppendHeader("Content-Disposition", "attachment; filename=bitmap.jpg");

bmp.Save(Response.OutputStream, ImageFormat.Jpeg);

There is also another approach using Response.BinaryWrite:

using (DataContext db = new DataContext())
    var objData = (from c in db.Companies where c.CompanyId == CompanyID select c.LogoSmall).FirstOrDefault();
    using (System.IO.MemoryStream stream = new System.IO.MemoryStream(objData.ToArray(), true))
        stream.Write(objData.ToArray(), 0, objData.ToArray().Length);
        Byte[] bytes = stream.ToArray();
        Response.ContentType = "image/jpeg";
        Response.AppendHeader("Content-Disposition", "attachment; filename=image1.jpg");

This will read the binary data from the database using Linq-to-Sql and will provide the file for saving appearing a SaveAs dialog with theimage1.jpg as the default filename.

Adding the Response.End() can be ommited, but you may inadvertently be sending other content back after Response.BinaryWrite() which may confuse the browser. Response.End() will ensure that that the browser only gets what you really intend.

Hope this helps to someone out there.

‘Till next time….Taaa daaaa.