MVC: Handling file uploads

ASP.Net MVC has some great abstractions over HTTP and the various Http classes, but one area which is currently lacking is around processing of file uploads.  Uploaded files are (still) available on the Request.Files collection and some posts suggest that you should go down this route.  And for testing, Scott Hanselman has provided some (fairly scary) mocking code.

Now Phil Haack has suggested a few improvements on this, but it’s still not as easy to test as I’d like (I’m a lazy developer, really) – I like to avoid mocks when I can since they can easily obscure the intention of the test.  Fortunately MVC model binders came to my rescue here.  Note that my web UI is only ever submitting one file.

DTO class:

public class UploadedFile
{
    public int Length { get; set; }
    public string ContentType { get; set; }
    public string FileName { get; set; }
    public Stream Stream { get; set; }
}

Model binder:

public class FileUploadBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var postedFile = controllerContext.HttpContext.Request.Files[0];
        if (postedFile == null) return null;
        return new UploadedFile
                   {
                       Length = postedFile.ContentLength,
                       ContentType = postedFile.ContentType,
                       FileName = postedFile.FileName,
                       Stream = postedFile.InputStream
                   };
    }
}

Controller action method:

[HttpPost, Authorize]
public ActionResult PeformFullUpload(string dataSourceName, 
            [ModelBinder(typeof(FileUploadBinder))] UploadedFile uploadedFile)

Test code:

[When("the user submits a file")]
public void PerformFileUpload()
{
    var uploadedFile = new UploadedFile{ Stream = _memoryStream};
    _viewResult = _controller.PeformFullUpload(_dataSourceName, _uploadedFile) as ViewResult;
}

Now that’s how I like my tests.   Smile

December 17 2011
blog comments powered by Disqus