arame3333
arame3333

Reputation: 10193

MVC file uploader returns a null

I am basing my solution on this article; http://dotnetslackers.com/articles/aspnet/ASP-NET-MVC-and-File-Uploads.aspx

However when I try to upload a picture I get a null instead of a filename.

My view looks like this;

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SHP.Models.HrViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Edit Employee
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
    <fieldset>
    <legend>Add details to the selected employee</legend>
    <p>The photo you select for an employee will appear on MNet.</p>
    <p>The qualifications you add for an employee will appear on their business cards when required.</p>
        <% using (Html.BeginForm("EditEmployee", "HumanResources", FormMethod.Post,
         new{enctype = "multipart/form-data"}))
           {%>
        <%: Html.AntiForgeryToken() %>
        <%: Html.ValidationSummary(true) %>
        <%: Html.EditorFor(model => model.EmployeeSelector) %>
        <% if (Model.SelectedEmployee != null)
           { %>
                <%: Html.HiddenFor(model => model.SelectedEmployee.EmployeeId) %>
                <%: Html.HiddenFor(model => model.EmployeeName) %>
                <table class="groupBorder" style="margin-top:15px; width:617px;">
                <tbody>
                <tr>
                    <th colspan="2">Add Details for <%: Model.EmployeeName %></th>
                </tr>
                <tr>
                    <td style="text-align: right;">
                <%: Html.LabelFor(model => model.SelectedEmployee.Photo)%>
                   </td>                    
                    <td>
                        <input type="file" id="Picture" name="Picture" />
                    </td>
                </tr>
                <tr>
                    <td style="text-align: right;">
                <%: Html.LabelFor(model => model.SelectedEmployee.Qualifications)%>
                   </td>                    
                    <td>
                <%: Html.TextBoxFor(model => model.SelectedEmployee.Qualifications, new {style = "width:500px;"})%>
                    </td>
                </tr>
                <tr>
                    <td colspan="2" style="text-align: center;padding-top:20px;">
                    <input type="submit" value="Save" id="btnSubmit" /></td>
                </tr>
                </table>
       <% } %>
        <% } %>
        </fieldset>
</asp:Content>

When you click on the Save button you go to this controller action;

    [HttpPost]
    [Authorize(Roles = "Administrator, HumanResources, ManagerAccounts, ManagerIT")]
    [ValidateAntiForgeryToken]
    [ValidateOnlyIncomingValues]
    public ActionResult EditEmployee(HrViewModel hrvm)
    {
        if (ModelState.IsValid)
        {
            if (hrvm.SelectedEmployee == null
                || hrvm.EmployeeSelector.SearchTextId != hrvm.SelectedEmployee.EmployeeId)
            {
                return this.RedirectToAction(
                    "EditEmployee", new { employeeId = hrvm.EmployeeSelector.SearchTextId });
            }

            if (hrvm.SelectedEmployee.Picture.HasFile())
            {
                var destinationFolder = Server.MapPath("/Users");
                var postedFile = hrvm.SelectedEmployee.Picture;
                var fileName = Path.GetFileName(postedFile.FileName);
                var path = Path.Combine(destinationFolder, fileName);
                postedFile.SaveAs(path);
                hrvm.SelectedEmployee.Photo = path;
            }   

            var emp = Employee.GetEmployee(hrvm.SelectedEmployee.EmployeeId);
            this.TryUpdateModel<IEmployeeHrBindable>(emp, "SelectedEmployee");
            emp.Update();
            this.TempData["Message"] = string.Format(
                "At {0} Details updated for {1}", DateTime.Now.ToString("T"), hrvm.EmployeeName);
            return this.View(hrvm);
        }

        return this.View(new HrViewModel());
    }

So what am I doing wrong?

Upvotes: 0

Views: 272

Answers (2)

Ian Routledge
Ian Routledge

Reputation: 4042

In your view use the following instead and the default model binding should work:

<%: Html.TextBoxFor(model => model.SelectedEmployee.Photo, new { type = "file" }) %>

That is assuming SelectedEmployee.Photo is of type HttpPostedFileBase.

The reason it isn't working at the moment is that the default model binder will be trying to find a property called Picture directly on the model, because that's the name of your file input. It won't find it, because Picture is a property of SelectedEmployee.

Changing it to what I've suggested above generates the correct id and name for the file input in the markup so when posted back has the correct path. This means the default model binder is then able to map between the form post value and the property.

Upvotes: 1

hxngsun
hxngsun

Reputation: 115

By default, MVC3 performs model binding based on the Name attribute of the input elements in your view.

To get file upload data, use the HttpPostedFileBase class as a parameter to your ActionResult and call the parameter 'file'.

[HttpPost]
[Authorize(Roles = "Administrator, HumanResources, ManagerAccounts, ManagerIT")]
[ValidateAntiForgeryToken]
[ValidateOnlyIncomingValues]
public ActionResult EditEmployee(HrViewModel hrvm, HttpPostedFileBase file)
{
    if (ModelState.IsValid)
    {
        if (hrvm.SelectedEmployee == null
            || hrvm.EmployeeSelector.SearchTextId != hrvm.SelectedEmployee.EmployeeId)
        {
            return this.RedirectToAction(
                "EditEmployee", new { employeeId = hrvm.EmployeeSelector.SearchTextId });
        }
        if (file.ContentLength > 0)
        {
            hrvm.SelectedEmployee.Picture = file;
            var destinationFolder = Server.MapPath("/Users");
            var postedFile = hrvm.SelectedEmployee.Picture;
            var fileName = Path.GetFileName(postedFile.FileName);
            var path = Path.Combine(destinationFolder, fileName);
            postedFile.SaveAs(path);
            hrvm.SelectedEmployee.Photo = path;
        }   

        var emp = Employee.GetEmployee(hrvm.SelectedEmployee.EmployeeId);
        this.TryUpdateModel<IEmployeeHrBindable>(emp, "SelectedEmployee");
        emp.Update();
        this.TempData["Message"] = string.Format(
            "At {0} Details updated for {1}", DateTime.Now.ToString("T"), hrvm.EmployeeName);
        return this.View(hrvm);
    }

    return this.View(new HrViewModel());
}

(So if you could grab the image data using model binding, it would have been located at hrvm.Picture instead of hrvm.SelectedEmployee.Picture)

Upvotes: 1

Related Questions