0

I'm new to ASP.Net (and webdev in general) and I'm trying to implement a site offering CV-like capabilities. One of such things would be to upload and view a CV photo of a person. Using scaffolding and a code sample found at SO: Uploading/Displaying Images in MVC 4 I managed to get as far as coming up with the following code where the problematic part is enclosed with a larger body of space:

<div class="form-horizontal">
<h4>Author</h4>
<hr />
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
@Html.HiddenFor(model => model.PersonID)
<div class="form-group">
    @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
    </div>
</div>
<div class="form-group">
    @Html.LabelFor(model => model.Picture, htmlAttributes: new { @class = "control-label col-md-2" })

    <div class="col-md-10">            
        @using (Html.BeginForm("FileUpload", "CV", FormMethod.Post, new { enctype = "multipart/form-data" }))
         {
             <input type="file" name="file" id="file" style="width: 100%;" />
             <br/>
             <input type="submit" value="Upload" class="submit" />
         }
    </div>

</div>
<div class="form-group">
    @Html.LabelFor(model => model.MobileNum, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.MobileNum, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.MobileNum, "", new { @class = "text-danger" })
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.Location, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.Location, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.Location, "", new { @class = "text-danger" })
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.LinkedIn, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.LinkedIn, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.LinkedIn, "", new { @class = "text-danger" })
    </div>
</div>

which is the relevant part of the view responsible for uploading, while the method in the Controller CV that I want to handle the request goes like this:

    [HttpPost]
    public ActionResult FileUpload(HttpPostedFileBase file)
    {
        if (file != null)
        {
            string pic = System.IO.Path.GetFileName(file.FileName);
            string path = System.IO.Path.Combine(
                                   Server.MapPath("~/Content/Images"), pic);
            // file is uploaded
            file.SaveAs(path);

            // save the image path path to the database or you can send image 
            // directly to database
            // in-case if you want to store byte[] ie. for DB
            using (MemoryStream ms = new MemoryStream())
            {
                file.InputStream.CopyTo(ms);
                byte[] array = ms.GetBuffer();
                //now I only have 1 mock author
                db.Authors.FirstOrDefault().Picture = array;
            }

        }
        // after successfully uploading redirect the user
        return View();
    }

My problem is that the post never hits this method for some reason despite having specified both the Action and the Controller name and VS being capable of connecting the View to the Controller (at least it swipes to the appropriate one on the Go To Controller command). I'm sure it's some rookie mistake, but I just can't seem to find the cause.

Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
oneManArmin
  • 606
  • 6
  • 24
  • 1
    The code you have shown should work fine. You html looks odd (having a `
    ` inside a `
    ` so best guess is you have nested forms which is invalid html and not supported
    –  Jan 30 '18 at 20:54
  • Thank you, that was the root of the problem. If you can convert it to an answer with a little explanation of some sort, I'm happy to accept it as an answer. – oneManArmin Jan 31 '18 at 09:03
  • You need to edit you question to show the outer form (otherwise the answer would not make much sense) –  Jan 31 '18 at 09:04
  • I added more code – oneManArmin Jan 31 '18 at 09:20

1 Answers1

1

The code in your question does not show it, but the fact you have form controls before and after the <form> element suggest you have an outer <form> element and explains why your inner <form> dos not hit the FileUpload() method.

Nested forms are invalid html and not supported. There is no guarantee what the behavior will be in different browsers or versions, but most browsers generate the name/value pairs for the successful form controls of the inner form, but post it to the action attribute of the outer form.

Its not clear why you want to upload the file separately and it would mean that if a user entered data in the other form controls and submitted the inner form, all that would be lost. It would be easier to remove the inner form and just post back the whole model including the file input to one controller method (by adding a HttpPostedFileBase file parameter to that method, or better, using a view model with a HttpPostedFileBase File property). Note that the form will need the enctype = "multipart/form-data" attribute.

If you do want to upload it separately, then one option would be to use ajax to submit the file input to the controller using FormData so at least any data the user has already entered will not be lost. Refer How to append whole set of model to formdata and obtain it in MVC for an example. In your case, you would initialize a new instance of FormData and .append() the value of the file input to it.

As a side note, your @Html.LabelFor(model => model.Picture) does not create a label associated with the file input (you do not have a form control with id="Picture")

  • **Yes, there is an outer form in my code**, but it is much further up, that is why I did not include it here. Uploading the picture with the other info would be fine, but I'm still figuring out how to make sense of the `Html Helper` classes in ASP. If you could direct me towards a good coherent solution as well, it would be much appreciated. – oneManArmin Jan 31 '18 at 10:23
  • Which bit are you not understanding? Other that the label I noted the rest of the view code you have shown look fine –  Jan 31 '18 at 10:24
  • What you should be doing is using a [view model](https://stackoverflow.com/questions/11064316/what-is-viewmodel-in-mvc) that includes a `public HttpPostedFileBase File { get; set; }` property and using `@Html.LabelFor(m => m.File) @Html.TextBoxFor(m => m.File, new { type="file" })` and then you can also add validation attributes and the `@Html.ValidationMessageFor(m => m.File)` - for example a [FileTypeAttribute](https://stackoverflow.com/questions/40199870/how-to-validate-file-type-of-httppostedfilebase-attribute-in-asp-net-mvc-4/40200034#40200034) or `FileSizeAttribute` –  Jan 31 '18 at 10:34
  • Not sure why you think you need to do that. You can save the file and save the data in the one action (are you saving the file to a file server and saving the path and files display name in the db, or are you saving the file in the db as a `byte[]`?) –  Jan 31 '18 at 10:36
  • I'm trying to save only the file as a byte[], I don't necessarily need the path. – oneManArmin Jan 31 '18 at 10:38
  • You deleted your previous comment :) What problem were you having? –  Jan 31 '18 at 10:41
  • Yes, sorry, I'm trying to experiment with the solution you proposed in the previous comment. I got as far as this: `
    @Html.LabelFor(model => model.Picture, htmlAttributes: new { @class = "control-label col-md-2" })
    @Html.TextBoxFor(model => Model.Picture, new { Class = "form-control", type ="file"}) @*@Html.ValidationMessageFor(model => model.Picture, "", new { @class = "text-danger" })*@
    ` For some reason though, this gives a not 64 based input error.
    – oneManArmin Jan 31 '18 at 11:06
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164255/discussion-between-stephen-muecke-and-rtech). –  Jan 31 '18 at 11:44
  • Thank you, don't need to, with the help of a cup of coffee, I managed to figure it out. Using some code samples you provided, I ended up writing a simple `
    ` with an image input field and tweaking my form to submit a `HttpPostedFileBase`as you suggested. Then, I handled the whole thing in that very `HttpPost` Edit method. it looks ugly AF, but the picture is now sitting on my DB as byte[].
    – oneManArmin Jan 31 '18 at 12:02
  • Did you read the comment in chat? And I'll add a few more tomorrow (its late here) –  Jan 31 '18 at 12:04