Welcome Guest, you are in: Login

Daisley-Harrison Software

RSS RSS

Navigation




Search the wiki
»

Downloading Files and Blobs As Attachments Using ASP.NET

RSS
Modified on 2009/12/10 23:21 by aarondh Categorized as Uncategorized

Here's is some sample code for getting asp.net applications to prompt for download an attachment file.

A good source for mapping file extensions to mime-types can be found at MIME Types.

It is important to specify both the mime-type in the asp.net response context and add a "Content-Disposition" header so that the browser will treat the data stream as an attachment.

default.aspx

The aspx page should only contain the page declaration. It should not contain any html. For example:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SampleWebAttachment._Default" %>

The code behind file is something like this:

default.aspx.cs

This is the bare minimum necessary to cause the browser to prompt with the "File Download" Dialog.

You must specify:
  • The ContentType (The mime-type of the content returned via the HTTP response)
  • Add the "Content-Disposition" HTTP header so the browser knows that the content is to be treated as an attachment.
  • Use WriteFile to directly write the files stream into the HTTP response.

File Download Dialog

   public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Context.Response.ContentType = "application/msword";
            Context.Response.AddHeader("Content-disposition", "attachment; filename=Something.docx");
            Context.Response.WriteFile(@"C:\Users\adaisleyharrison\Documents\Visual Studio 2008\Projects\SampleWebAttachment\SampleWebAttachment\files\Something.docx");
            Context.Response.End();
        }
    }

Downloading binary data streams

Generalizing this to download such any stream data data look something like this:

    public partial class DownloadAStream : System.Web.UI.Page
    {
        public const int DEFAULT_BUFFER_SIZE = 4096;
        protected void Page_Load(object sender, EventArgs e)
        {
            Context.Response.ContentType = "application/msword";
            Context.Response.AddHeader("Content-disposition", "attachment; filename=Something.docx");

            FileStream stream = File.Open(@"C:\Users\adaisleyharrison\Documents\Visual Studio 2008\Projects\SampleWebAttachment\SampleWebAttachment\files\Something.docx", FileMode.Open);

            writeStream(stream);

            Context.Response.End();
        }

        private void writeStream(Stream stream)
        {
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];

            try
            {
                int bytesRead;
                while ( (bytesRead = stream.Read( buffer, 0, buffer.Length )) > 0 )
                {
                    Context.Response.OutputStream.Write(buffer, 0, bytesRead);
                }
            }
            finally
            {
                stream.Close();
            }
        }
    }

The writeStream function is really nothing but a data pump. It reads from one stream and writing to the HTTP OutputStream.

Mime-Type Manager Class

If you are mapping several different file types you may want to create a lookup class to help with the mapping of file extension to mime-type. Here is a simple manager class that can be used to map between file extensions and mime-types:

    public class MimeTypeManager
    {
        private Dictionary<string, string> _mimeTypeByExtension;
        public const string DEFAULT_MIME_TYPE = "application/octet-stream";
        public string DefaultMimeType { get; set; }

        public MimeTypeManager()
        {
            this.DefaultMimeType = DEFAULT_MIME_TYPE;
            _mimeTypeByExtension = new Dictionary<string,string>();
        }
        public void RegisterMimeType( string extension, string mimeType )
        {
            mimeType = mimeType.ToLowerInvariant();

            _mimeTypeByExtension.Add(extension, mimeType);
        }
        public string ToMimeType(string extension)
        {
            
            string mimeType;

            extension = extension.ToLowerInvariant();

            if (!_mimeTypeByExtension.TryGetValue(extension, out mimeType))
            {
                mimeType = this.DefaultMimeType;
            }
            return mimeType;
        }
    }

With this helper class we can further generize download page functionality as follows:

    public partial class GenericDownload : System.Web.UI.Page
    {
        public const int DEFAULT_BUFFER_SIZE = 4096;
        private static MimeTypeManager _mimeTypeManager;

        static GenericDownload()
        {
            _mimeTypeManager = new MimeTypeManager();
            _mimeTypeManager.RegisterMimeType(".doc", "application/msword");
            _mimeTypeManager.RegisterMimeType(".docx", "application/msword");
            /*... more definitions here ...*/
            _mimeTypeManager.RegisterMimeType(".htm", "text/html");
            _mimeTypeManager.RegisterMimeType(".html", "text/html");
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            string filePath = @"C:\Users\adaisleyharrison\Documents\Visual Studio 2008\Projects\SampleWebAttachment\SampleWebAttachment\files\Something.docx";

            try
            {
                FileStream stream = File.Open(filePath, FileMode.Open);

                writeAttachment(stream, filePath);
            }
            catch (FileNotFoundException exception)
            {
                Context.Response.Write("<html><body><h1>File Not Found</h1></body></html>");
                Context.Response.End();
            }

        }

        private void writeAttachment(Stream stream, string filePath)
        {
            string filename = Path.GetFileName(filePath);
            string extension = Path.GetExtension(filePath);
            Context.Response.ContentType = _mimeTypeManager.ToMimeType(extension);
            Context.Response.AddHeader("Content-disposition", "attachment; filename=" + filename);
            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];

            try
            {
                int bytesRead;
                while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    Context.Response.OutputStream.Write(buffer, 0, bytesRead);
                }
            }
            finally
            {
                stream.Close();
            }
            Context.Response.End();
        }
    }


Downloading SQL Blobs as Attachments

Here is a slight modification for reading blobs from SQL and downloading them as attachments:


    public partial class downloadblog : System.Web.UI.Page
    {
        private SqlConnection _sqlConnection;
        public const int DEFAULT_BUFFER_SIZE = 4096;
        private static MimeTypeManager _mimeTypeManager;

        static downloadblog()
        {
            _mimeTypeManager = new MimeTypeManager();
            _mimeTypeManager.RegisterMimeType(".doc", "application/msword");
            _mimeTypeManager.RegisterMimeType(".docx", "application/msword");
            _mimeTypeManager.RegisterMimeType(".word", "application/msword");
            _mimeTypeManager.RegisterMimeType(".w6w", "application/msword");
            _mimeTypeManager.RegisterMimeType(".mdb", "application/msaccess");
            _mimeTypeManager.RegisterMimeType(".xla", "application/msexcel");
            _mimeTypeManager.RegisterMimeType(".xls", "application/msexcel");
            /* ... more mime types ...*/
            _mimeTypeManager.RegisterMimeType(".cpp", "text/plain");
            _mimeTypeManager.RegisterMimeType(".vb", "text/plain");
            _mimeTypeManager.RegisterMimeType(".aspx", "text/xml");
            _mimeTypeManager.RegisterMimeType(".xml", "text/xml");
            _mimeTypeManager.RegisterMimeType(".xslt", "text/xml");
            _mimeTypeManager.RegisterMimeType(".xsl", "text/xml");
            _mimeTypeManager.RegisterMimeType(".htm", "text/html");
            _mimeTypeManager.RegisterMimeType(".html", "text/html");
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            string blobId = Context.Request["blobId"];
            string blobFilePath;

            SqlCommand getBlobRecord = new SqlCommand(
                "SELECT Blob, FilePath "+
                "FROM BlobTable "+
                "WHERE BlobIndex = @blobId ", _sqlConnection);

            getBlobRecord.Parameters.Add("@blobId",  SqlDbType.NVarChar, 20).Value = blobId;


            // Open the connection and read data into the DataReader.
            _sqlConnection.Open();
            SqlDataReader blobTableReader = getBlobRecord.ExecuteReader(CommandBehavior.SequentialAccess);
            try
            {
                if (blobTableReader.Read())
                {
                    blobFilePath = blobTableReader.GetString(1);
                    writeBlobAsAttachment(blobTableReader, 0, blobFilePath);
                }
            }
            finally
            {
                blobTableReader.Close();
            }
 
        }

        public void writeBlobAsAttachment(DbDataReader dataReader, int blobColumnIndex, string filePath)
        {

            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];

            string filename = Path.GetFileName(filePath);

            string extension = Path.GetExtension(filePath);

            long startIndex = 0; //Start of the blob

            long bytesRead;

            Stream outputStream = Context.Response.OutputStream;

            Context.Response.ContentType = _mimeTypeManager.ToMimeType(extension);
            Context.Response.AddHeader("Content-disposition", "attachment; filename=" + filename);

            // Continue reading and writing while there are bytes to be read.
            while ((bytesRead = dataReader.GetBytes(blobColumnIndex, startIndex, buffer, 0, buffer.Length)) > 0)
            {
                outputStream.Write(buffer, 0, (int)bytesRead);

                // Reposition the start index to the end of the last buffer and fill the buffer.
                startIndex += bytesRead;
            }
        }
    }


Sample code for this example is available for download here: SampleWebAttachment Code



  Name Size
- FileDownload.png 24.66 KB
- SampleWebAttachment.zipx 142.97 KB





Please visit our blog at http://blog.daisley-harrison.com

- All Content © Copyright 2010 Daisley-Harrison Software - All Rights Reserved. - ScrewTurn Wiki version 3.0.1.400.