Here is a common question that I hear frequently: "How do I download a file from a Web site, but instead of displaying it in the browser see it as a file that can be saved (ie. see the Save As dialog)?"

Normally, when you link a file that file will always display inside of the browser because the browser loads it and automatically determines the content type based on the file extension. The Web Server provides a content type based on mime-type mappings, and based on that content type the browser serves the page and displays it.

So when you click on a link like a jpg image the browser knows it's an image and will display that image. You can of course always use the browser short cut menu and use the Save Target As option to save the file to disk.

The Content-Disposition Header

The key element to allow a file to be treated as a file rather than content to be displayed in the browser is the Content-Disposition header.

The Content-Disposition header basically allows you to specify that the response you're sending back is to be treated as an attachment rather than content which results in the download dialog.

In ASP.NET you can add the Content-Disposition header like this:

Response.AppendHeader("Content-Disposition", "attachment; filename=SailBig.jpg");

You specify that the output is an attachment and the name that is to be displayed on the download dialog (or the actual downloaded file if auto-download is used).

Here's what this dialog looks like in FireFox when you specify a Content-Disposition header:

SaveAsDialog

Note that this behavior varies from browser to browser though. Firefox has this nice dialog that gives you choices. Internet Explorer shows the yellow bottom bar asking whether you want to save the file. Chrome - depending on the options - will simply download the file to your Downloads folder without prompting for anything.

Sending a Binary File to the Client

If you want to force a Save As dialog automatically when a link is clicked via code from the server side, you have to send the file back through code using Response.TransmitFile(), Response.BinaryWrite() or streaming it into the Response.OutputStream and add a couple of custom headers to the output.

The optimal way to do this is to use Response.TransmitFile() to explicitly send the file from your ASP.NET application and then add the Content Type and Content-Disposition headers. Note thought that Response.TransmitFile() in recent versions of IIS can only serve files out of the virtual folder hierarchy of the Web site or virtual. For files outside of the virtual path you have to stream into the OutputStream().

Assuming your file does live inside of the folder hierarchy here's how you can force the Save As dialog for output sent with TransmitFile():


Response.ContentType = "image/jpeg";
Response.AppendHeader("Content-Disposition", "attachment; filename=SailBig.jpg");
Response.TransmitFile(Server.MapPath("~/images/sailbig.jpg"));
Response.End();

This will cause a Open / Save As dialog box to pop up with the filename of SailBig.jpg as the default filename preset.

Can't use TransmitFile?

The previous example used TransmitFile() which is most efficient, but you don't always have files to work with or you may have files that live outside of the directory structure of your Web site which TransmitFile() doesn't allow for. Luckily there are other ways to send binary data to the client from ASP.NET code.

To write binary data that is a byte[] you can use the Response.BinaryWrite() method to write out binary data. If you have a Stream of binary data, you can stream the data directly into the into the Response.OutputStream.

Here's an example of streaming an image directly into the Response.OutputStream from a Bitmap:

Bitmap bmp = ImageUtils.CornerImage(); // retrieve an image

Response.ContentType = "image/jpeg";
Response.AppendHeader("Content-Disposition", "attachment; filename=LeftCorner.jpg");

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

This code simply uses the bmp.Save() method which writes a binary stream into the Response.OutputStream which results in the binary data being sent to the client. Any Stream features (like Stream.Copy) behaviors can be applied to the OutputStream to stream data to the client.

If you're dealing with files, try to avoid streams if you can and use TransmitFile() instead. TransmitFile is very efficient because it offloads the file streaming to IIS which happens off an I/O port so the file streaming releases the IIS request thread while returning the file. This is especially useful when streaming large files to the client as it doesn't tie up the IIS pipeline.

Saving rather than Viewing

Browsers are focused on displaying content within the browser's viewport. When you do need to download content rather than view Content-Disposition is your friend… I hope you found this short post useful.