13

In my controller I have, because I wanted to be able to fill out some details about the video and actually upload it, the Video class doesn't need the actual video because it's going to be passed to another web service.

public class VideoUploadModel
    {
        public HttpPostedFileBase vid { get; set; }
        public Video videoModel { get; set; }
    }

    //
    // POST: /Video/Create
    [HttpPost]
    public ActionResult Create(VideoUploadModel VM)
    {
        if (ModelState.IsValid)
        {
            db.Videos.AddObject(VM.videoModel);
            db.SaveChanges();
            return RedirectToAction("Index");  
        }

        ViewBag.UserId = new SelectList(db.DBUsers, "Id", "FName", VM.videoModel.UserId);
        return View(VM);
    }

and in my view I have

@model LifeHighlightsShoeLace.Controllers.VideoController.VideoUploadModel
@{
   ViewBag.Title = "Create";
}

<h2>Create</h2>

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
@using (Html.BeginForm("Create", "Video", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.ValidationSummary(true)
<fieldset>
    <legend>Video</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.videoModel.KalturaID)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.videoModel.KalturaID)
        @Html.ValidationMessageFor(model => model.videoModel.KalturaID)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.videoModel.Size)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.videoModel.Size)
        @Html.ValidationMessageFor(model => model.videoModel.Size)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.videoModel.Date)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.videoModel.Date)
        @Html.ValidationMessageFor(model => model.videoModel.Date)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.videoModel.UploadedBy)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.videoModel.UploadedBy)
        @Html.ValidationMessageFor(model => model.videoModel.UploadedBy)
    </div>

    <div class="editor-label">
        @Html.LabelFor(model => model.videoModel.UserId, "User")
    </div>

    <div class="editor-field">
        @Html.DropDownList("UserId", String.Empty)
        @Html.ValidationMessageFor(model => model.videoModel.UserId)
    </div>
    <div class="editor-field">
    <input name="model.vid" type="file" />
    </div>
    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>

}

When I submit the form the videoModel part of VM is filled out but vid the actual file is null. Any ideas?

Matthew Arkin
  • 4,460
  • 2
  • 27
  • 28

4 Answers4

19

Update according to OP comment

set the Max file length in the web.config file Change the "?" to a file size that you want to be your max, for example 65536 is 64MB

<configuration>
  <system.web>
    <httpRuntime maxRequestLength="?" /> 
  </system.web>
</configuration>

You can't add the file to the model, it will be in it's own field not part of the model

<input name="videoUpload" type="file" />

Your action is incorrect. It needs to accept the file as it's own parameter (or if multiple use IEnumerable<HttpPostedFileBase> as the parameter type)

[HttpPost]
public ActionResult Create(VideoUploadModel VM, HttpPostedFileBase videoUpload)
{
    if (ModelState.IsValid)
    {
        if(videoUpload != null) { // save the file
            var serverPath = server.MapPath("~/files/" + newName);
            videoUpload.SaveAs(serverPath);
        }

        db.SaveChanges();
        return RedirectToAction("Index");  
    }

    ViewBag.UserId = new SelectList(db.DBUsers, "Id", "FName", VM.videoModel.UserId);
    return View(VM);
}

If you were allowing multiple files to be selected you have to allow for that

[HttpPost]
public ActionResult Create(VideoUploadModel VM, IEnumerable<HttpPostedFileBase> videoUpload)
{
    if (ModelState.IsValid)
    {
        if(videoUpload != null) { // save the file
            foreach(var file in videoUpload) {
                var serverPath = server.MapPath("~/files/" + file.Name);
                file.SaveAs(serverPath);
            }
        }

        db.SaveChanges();
        return RedirectToAction("Index");  
    }

    ViewBag.UserId = new SelectList(db.DBUsers, "Id", "FName", VM.videoModel.UserId);
    return View(VM);
}
CD Smith
  • 6,597
  • 7
  • 40
  • 66
  • Thanks (and @BuildStarted). Now when I click create IE gives me a "IE cannot display the webpage" error and chrome tells me that the connection to localhost was interrupted. If I don't include a file it posts with no issue. – Matthew Arkin Jun 01 '12 at 19:52
  • Sounds like you selected a file larger than 4mb – CD Smith Jun 01 '12 at 19:55
  • Updated the answer check it out – CD Smith Jun 01 '12 at 19:58
  • Thanks, I'm guessing for large file uploads I'd be better off using some sort of ajax/flash/silverlight file uploader? – Matthew Arkin Jun 01 '12 at 20:01
  • No, it's a browser thing, you'd have to set that no matter what to allow larger files to be accepted – CD Smith Jun 01 '12 at 20:01
  • We had the same problem with large PDF files so had to up the request size – CD Smith Jun 01 '12 at 20:02
  • Thanks, this is going to be for uploading some video files so I'm sure the request size is going to need to be set very high. – Matthew Arkin Jun 01 '12 at 20:17
  • Nowadays you can add the file(s) in the model. Input must reference the name of the property, in this case "vid". Even better you can improve it using c#'s expression "nameof". E.g: "" so you can mantain homogeneity. – Bazil Oct 18 '22 at 21:53
2

The reason it isn't binding is because the model binder only looks at QueryString, Form, and RouteData when binding a complex model such as yours. The way to get around this is to have another parameter in your action method. (change your "name" to just "vid" as well)

[HttpPost]
public ActionResult Create(VideoUploadModel VM, HttpPostedFileBase vid)
{
    //add your vid to the model or whatever you want to do with it :)

    if (ModelState.IsValid)
    {
        db.Videos.AddObject(VM.videoModel);
        db.SaveChanges();
        return RedirectToAction("Index");  
    }

    ViewBag.UserId = new SelectList(db.DBUsers, "Id", "FName", VM.videoModel.UserId);
    return View(VM);
}
Buildstarted
  • 26,529
  • 10
  • 84
  • 95
2

Works for me:

public class CreateVeiwModel
    {
        public Speaker Speaker { get; set; }
        public Guid Guid { get; set; }
        public HttpPostedFileBase File { get; set; }
    }

Controller:

[HttpPost]
        public ActionResult Create(CreateVeiwModel model)
        {
            if (ModelState.IsValid)
            try
            {
                Repository.AddSpeaker(model.Speaker);
            ...
        }

View:

@Html.Label("Most Up-to-Date CV")
             <input type="file" name="File"/>

I think solution placed there: Model.File ~ <input name="File"/>

AZIP
  • 21
  • 2
0

change

<input name="model.vid" type="file" />

to

@Html.TextBoxFor(model => model.vid, new {type="file"}) 

depending on what else is on your page, and where the view is being rendered, the MVC will generate unique ID's, I think your hard coded ID is not correctly associated with the form fields.

Jason Kulatunga
  • 5,814
  • 1
  • 26
  • 50