FileResult

With the release of ASP.NET MVC Preview 3, we now have the ability to manage how results are returned to the client.  Before, you pretty much had to create your own route handler in order to intercept the HTTP response pipeline.  Now, all you have to do is subclass ActionResult and override the ExecuteResult(ControllerContext) method.

As an example, here's an implementation which I'm using to return files back to the user -- Silverlight XAPs, my resume as DOCX, etc.

In-use, it's a simple call:

public FileResult GetFileData(int id)
{
    PostFile file = null;

    using (var db = new ThubanDataContext())
    {
        file = db.Files.Where(x => x.PostFileId == id).Single();
    }

    return new FileResult(file.Name, file.MimeType, file.Data.ToArray());
}

Enjoy.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Web;
using System.Web.Mvc;

namespace Thuban.Web.Mvc
{
    public class FileResult : ActionResult
    {
        #region Constructors

        public FileResult(string fileName, string contentType, IEnumerable<byte> data)
            : this(fileName, contentType, data, null)
        {
        }

        public FileResult(string fileName, string contentType, IEnumerable<byte> data, 
IDictionary<string, string> headers) { this.FileName = fileName; this.ContentType = contentType; this.Data = data; this.Headers = headers; } public FileResult(string fileName, string contentType, Encoding contentEncoding,
IEnumerable<byte> data) : this(fileName, contentType, contentEncoding, data, null) { } public FileResult(string fileName, string contentType, Encoding contentEncoding,
IEnumerable<byte> data, IDictionary<string, string> headers) : this(fileName, contentType, data, headers) { this.ContentEncoding = contentEncoding; } public FileResult(string fileName, string contentType, string path) : this(fileName, contentType, path, null) { } public FileResult(string fileName, string contentType, string path,
IDictionary<string, string> headers) { this.FileName = fileName; this.ContentType = contentType; this.Data = ReadBytes(path); this.Headers = headers; } #endregion IEnumerable<byte> ReadBytes(string path) { var stream = File.OpenRead(path); var bytes = new byte[1024]; int n; while ((n = stream.Read(bytes, 0, bytes.Length)) != 0) { for (int i = 0; i < n; i++) { yield return bytes[i]; } } } // Methods public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } HttpResponseBase response = context.HttpContext.Response; response.Clear(); response.ClearContent(); if (!string.IsNullOrEmpty(this.ContentType)) { response.ContentType = this.ContentType; } else { response.ContentType = MediaTypeNames.Text.Plain; } response.ContentEncoding = this.ContentEncoding ?? Encoding.Default; if (!String.IsNullOrEmpty(this.FileName)) { response.AppendHeader("content-disposition", "attachment; filename=" + this.FileName); } if (this.Headers != null) { foreach (var header in Headers) { response.AppendHeader(header.Key, header.Value); } } if (this.Data != null) { response.BinaryWrite(this.Data.ToArray()); } } // Properties public IEnumerable<byte> Data { get; set; } public string FileName { get; set; } public Encoding ContentEncoding { get; set; } public string ContentType { get; set; } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public IDictionary<string, string> Headers { get; set; } } }