Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC: How to post File Upload and other form fields to one action

I am creating a document library application with a DocumentController that needs to upload a thumbnail image of each doument in the library. I want to keep the File Upload field on the same Create/Edit form as the other fields (Title, Description, CategoryId etc).
The problem is I'm not sure if I can mix or nest the form tags for

Html.BeginForm("Create", "Document", FormMethod.Post, enctype = "multipart/form-data") 

and

Html.BeginForm() 

My view is as follows:

 <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Publications.WebUI.Models.DocumentEditViewModel >" %>  <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">     Edit </asp:Content> <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">     <fieldset>         <legend>Edit             <%=  Html.Truncate(Model.Document.Title, 50)%></legend>         <%= Html.ValidationSummary(false) %>         <% using (Html.BeginForm())            { %>         <div class="editor-label">             <%= Html.LabelFor(model => model.Document.Title) %>         </div>         <div class="editor-field">             <%= Html.HiddenFor(model => model.Document.DocumentId ) %>             <%= Html.ValidationMessageFor(model => model.Document.Title) %>             <%= Html.TextBoxFor(model => model.Document.Title)%>         </div>         <div class="editor-label">             <%= Html.LabelFor(model => model.Document.DocumentUrl)%>         </div>         <div class="editor-field">             <%= Html.ValidationMessageFor(model => model.Document.DocumentUrl)%>             <%= Html.TextBoxFor(model => model.Document.DocumentUrl)%>         </div>         <div class="editor-label">             <%= Html.LabelFor(model => model.Document.Description)%>         </div>         <div class="editor-field">             <%= Html.ValidationMessageFor(model => model.Document.Description)%>             <%= Html.TextAreaFor(model => model.Document.Description) %>         </div>         <div class="editor-label">             <%= Html.LabelFor(model => model.Document.ThumbnailUrl )%>         </div>         <div class="editor-field">             <% using (Html.BeginForm("Create", "Document",                     FormMethod.Post, new { enctype = "multipart/form-data" }))                {%>             <%= Html.ValidationMessageFor(model => model.Document.ThumbnailUrl )%>             <input name="uploadFile" type="file" />             <% } %>         </div>         <div class="formActions">             <div class="backNav">                 <%= Html.ActionLink("< Back to List", "Index") %>             </div>             <div class="submit">                 <input type="submit" value="Save" />             </div>             <% } %>         </div>     </fieldset> </asp:Content> 

My controller just takes the Document model and HttpPostedFileBase and tries to upload the file to the server and save the Document to the repository

 [HttpPost]  public ActionResult Create(Document document, HttpPostedFileBase uploadFile)  {       if (ModelState.IsValid)      {          //Process file upload          //Update repository        }         return View("List");   } 

So I'm wondering if it is possible to do the file upload and update the repository on the same action and how should I structure my View to facilitate this.

like image 698
Brendan Avatar asked Mar 01 '11 00:03

Brendan


1 Answers

I had a look in Steve Sanderson's great book (Pro ASP.NET MVC 2 Framework) and his Sports Store sample application has a file upload form where there are standard form elements mixed with a file upload "multipart/form-data" element. So it looks like the multipart type suffices for all form elements on the page. Although the uploaded image is being saved in the db I'm sure I can do a file.SaveAs() within the same Action. Thanks Mr. Sanderson. Hope you dont mind me reproducing your code...

VIEW

    <asp:Content ContentPlaceHolderID="MainContent" runat="server">     <h1>Edit <%= Model.Name %></h1>      <% using (Html.BeginForm("Edit", "Admin", FormMethod.Post,                               new { enctype = "multipart/form-data" })) { %>         <%= Html.Hidden("ProductID") %>         <p>             Name: <%= Html.TextBox("Name") %>             <div><%= Html.ValidationMessage("Name") %></div>         </p>         <p>             Description: <%= Html.TextArea("Description", null, 4, 20, null) %>             <div><%= Html.ValidationMessage("Description") %></div>         </p>         <p>             Price: <%= Html.TextBox("Price") %>             <div><%= Html.ValidationMessage("Price") %></div>         </p> <p>     Category: <%= Html.TextBox("Category") %>     <div><%= Html.ValidationMessage("Category") %></div> </p> <p>     Image:     <% if(Model.ImageData == null) { %>         None     <% } else { %>         <img src="<%= Url.Action("GetImage", "Products",                                   new { Model.ProductID }) %>" />     <% } %>     <div>Upload new image: <input type="file" name="Image" /></div>                 </p>  <input type="submit" value="Save" /> &nbsp;&nbsp;         <%=Html.ActionLink("Cancel and return to List", "Index") %>     <% } %> </asp:Content> 

CONTROLLER

    [AcceptVerbs(HttpVerbs.Post)]     public ActionResult Edit(Product product, HttpPostedFileBase image)     {         if (ModelState.IsValid) {             if (image != null) {                 product.ImageMimeType = image.ContentType;                 product.ImageData = new byte[image.ContentLength];                 image.InputStream.Read(product.ImageData, 0, image.ContentLength);             }             productsRepository.SaveProduct(product);             TempData["message"] = product.Name + " has been saved.";             return RedirectToAction("Index");         }         else // Validation error, so redisplay same view             return View(product);     } 
like image 161
Brendan Avatar answered Sep 27 '22 20:09

Brendan