How to support file uploads in ASP.Net MVC

Published 3 June 9 4:57 PM | Phil Gilmore

MVCFirefoxLogo 06/03/2009, Phil Gilmore

ASP.NET MVC can support file uploads.  You need two components to support file uploads. 

  • A form in your markup (view) which contains an <input type="file"...> tag and which has the proper enctype attribute.
  • A controller action which will receive the upload information and perform a task with it.

 

Your form can be as simple as this:

 
<form action="/MyController/SendFile" enctype="multipart/form-data" method="post">
    <input type="file" id="SourceFile" name="SourceFile" />
    <br />
    <input type="submit" value="Send" name="btnUpload" id="Submit1" />
</form>

 

Notice the enctype attribute on the <form...> tag.  This is required to support file uploads.  Notice too that the action points to our controller action which knows how to work with the uploaded file.  Lastly, you'll see the <input type="file...> tag inside the form.

 

Here is a screen shot of the form, rendered in Firefox 3.0.

UploadForm

 

The form as shown above contains only Html and is fairly universal regardless of the web framework you are using.  This means it should be familiar to you.  You have probably seen the enctype attribute on a form with its accompanying. The id attribute of the <input type="file"... > tag will be mapped by the MVC framework to a property of your model or to a parameter in the action method (in this case the SendFile action).  Here is an example of an action which takes the file as a parameter.

 

public ContentResult SendFile(HttpPostedFileBase SourceFile)
{

}

 

This action gets an HttpPostedFileBase object containing properties for the original local filename and the content of the file itself.  Notice that the MVC framework only maps this parameter to the file data if the parameter name (SourceFile) matches the id and name parameters from the <input type="file"...> tag in the form.

 

If you have a strange circumstance which prohibits you from organizing your form and action in this way and MVC cannot map the file data to an HttpPostedFileBase parameter, you can retrieve it manually from the Request object.

 

public ContentResult SendFile()
{
    HttpPostedFileBase SourceFile = Request.Files[0];
    //...
}

Here is an evaluation of the HttpPostedFileBase object as provided to the SendFile action method.

UploadFilesQuickwatch

 

Once you have an HttpPostedFileBase object, you can work with the file data.  Here are the members of the HttpPostedFileBase class.

 

protected HttpPostedFileBase();
public virtual int ContentLength { get; }
public virtual string ContentType { get; }
public virtual string FileName { get; }
public virtual Stream InputStream { get; }
public virtual void SaveAs(string filename);

 

This is pretty simple stuff.  You can save the file, read the file or get the file's name and mime type (ContentType).  ContentLength is generally not necessary since the InputStream already contains the data of the correct size.

Here is a working action that displays the content of any file uploaded to it.

 

public ContentResult SendFile()
{
    if (Request.Files.Count == 0)
        return new ContentResult() { ContentType = "text/plain", Content = "File upload failed." };
    else
    {
        HttpPostedFileBase SourceFile = Request.Files[0];
        ContentResult result = new ContentResult();
        result.ContentType = "text/plain";

        StreamReader reader = new StreamReader(SourceFile.InputStream);
        string content = reader.ReadToEnd();
        result.Content = content;

        return result;
    }
}

This method doesn't use the MVC-mapped HttpPostedFileBase parameter.  This is not as elegant, but doesn't require a matching name or id on the file upload element in the form markup.  This may be useful in some cases.

Lastly, entering that <input type="file"...> markup into the form can be error-prone.  While the element is not large or hard to read, it must be exact and is inconsistent with styles supporting strongly-typed views mapped to an MVC model.  Therefore it may be prudent to extend the HtmlHelper class to make this easier, cleaner and more consistent.  I have written an extension method to do this.  It has 2 overloads.

 

public static string Upload(this HtmlHelper helper, string name)
{
    string result = string.Format("<input type=\"file\" id=\"{0}\" name=\"{0}\" />", name);
    return result;
}

public static string Upload(this HtmlHelper helper, string name, object htmlAttributes)
{
    string attributes;
    if (htmlAttributes is IEnumerable<KeyValuePair<string, object>>)
        attributes = (htmlAttributes as IEnumerable<KeyValuePair<string, object>>).ToAttributeString();
    else
        attributes = (new RouteValueDictionary(htmlAttributes)).ToAttributeString();

    string result = string.Format("<input type=\"file\" name=\"{0}\" {1} />", name, attributes);
    return result;
}

Using this extension, my markup can be changed to this:

 

<% using (Html.BeginForm("SendFile", "MyController", FormMethod.Post, new { id = "sendFileForm", enctype = "multipart/form-data" }))

{ %>

    <%= Html.Upload("SourceFile", new { style="width: 300px;" })%>

    <br />

    <input type="submit" value="Send" name="btnUpload" id="Submit1" />

<% } %>

 

Personally, I don't think this is as pretty as straight HTML, but it's easier to write consistently, and if you're using MVC, your whole page probably looks like this already anyway.  It's your choice.  Either way you build the markup, the controller action is unaffected. 

 

None of this is very complicated but I recently had to run through it and construct a proof-of-concept for some coworkers and figured I'd publish it for the masses.  I hope it was helpful.

Comments

# ASP.NET MVC Archived Blog Posts, Page 1 said on June 7, 2009 10:27 PM:

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

# Ping said on December 3, 2009 8:58 AM:

Do you know if there is a way to customize the 'Browse...' button beside the file uploading text box? For example, I want it in a special color, and special font.

# BoillaZooto said on December 27, 2009 11:46 AM:

Another useful and informative post. Thanks !

# rimonabantexcellence site title said on June 5, 2013 5:54 PM:

Pingback from  rimonabantexcellence site title