File upload in MVC when used in bootstrap modal returns null
Asked Answered
R

6

8

I'm trying to upload images to my application but it always returns null. I'm unable to find the issue here. Can you help me out? Here's my code.

Model


[Table("Slider")]
public partial class Slider : BaseModel
{
    [Required]
    [StringLength(200)]
    public string FileName { get; set; }

    [StringLength(200)]
    public string Title { get; set; }

    [StringLength(1000)]
    public string Description { get; set; }

    public int? Order { get; set; }
}


[NotMapped]
public class SliderImage : Slider
{
    public HttpPostedFileBase ImageFile { get; set; }
}

View


@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.AntiForgeryToken()
<div class="modal-body">
    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Id)

        <div class="form-group">
            @Html.LabelFor(model => model.FileName, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.FileName, new { @class = "form-control", @readonly = "readonly" })
                @Html.ValidationMessageFor(model => model.FileName, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.ImageFile, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.ImageFile, new { @class = "form-control", type = "file" })
               //This is Same as below
               //<input class="form-control" id="ImageFile" name="ImageFile" type="file" value="">
            </div>
        </div>

Controller


    public ActionResult Edit(int id)
    {
        Slider slider = _db.Sliders.Find(id);
        if (slider == null)
        {
            return HttpNotFound();
        }
        Mapper.CreateMap<Slider, SliderImage>();
        SliderImage sliderImage = Mapper.Map<Slider, SliderImage>(slider);
        return PartialView("_Edit", sliderImage);
    }


    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult EditSlider([Bind(Include = "Id,FileName,Title,Description,Order,IsActive,Name,ImageFile")] SliderImage sliderImage)
    {
        if (ModelState.IsValid)
        {
            Mapper.CreateMap<SliderImage, Slider>();
            Slider slider = Mapper.Map<SliderImage, Slider>(sliderImage);
            _db.Entry(slider).State = EntityState.Modified;
            _db.SaveChanges();
            return Json(new { success = true });
        }
        return PartialView("_EditSlider");
    }

What am I doing wrong i this?


Found The Issue

I'm binding the partial view inside the bootstrap modal popup. When I upload from the popup, the upload returning null. Instead if I open the partial View directly in browser, then the file is present in the model. So there is no problem with file upload. The problem is with modal popup or something.

When Using Bootstrap model When Using Bootstrap model

When using partial View Directy When using partial View Directy

Check the difference found when using fiddler between the Bootstrap Modal Submit and Using the partial View Directly in the following image respectively

Comparison of Fiddler requests

When posting from the modal popup, the content type is changed to application/x-www-form-urlencoded where as when using the partial view directly it is multipart/form-data


Found the root Issue.

$('form', dialog).submit(function () {
                    var $form = $(this);
                    var enctype = $form.attr('id');
                    $.ajax({
                        url: this.action,
                        type: this.method,
                        data: $(this).serialize(),
                        success: function (result) {
                            if (result.success) {
                                $('#myModal').modal('hide');
                                //Refresh
                                location.reload();
                            } else {
                                $('#myModalContent').html(result);
                                bindForm();
                            }
                        }
                    });
                    return false;
                });

I'm using AJAX posting to submit the data from my form. When using $(this).serialize() the ajax success is being called but the file is not returning as the content type is different. How can I change this??

Runthrough answered 25/10, 2014 at 14:8 Comment(2)
I am facing the same issue would you like to post the solution if you get the solution .Pinnatipartite
What's the solution?Directorate
S
2

I think I have been able to identify your problem, Ajax does not support file serialization, you should use the following method in the script:

$('form', dialog).submit(function () {
        var formData = new FormData($(this)[0]);
        $.ajax({
            url: this.action,
            type: this.method,
            contentType: this.enctype,
            data: formData,
            success: function (result) {
                if (result.success) {
                    $('#myModal').modal('hide');
                    $('#replacetarget').load(result.url); //  Load data from the server and place the returned HTML into the matched element
                } else {
                    $('#myModalContent').html(result);
                    bindForm(dialog);
                }
            }
        });
        return false;
    });
Sumption answered 30/11, 2016 at 10:45 Comment(0)
J
1

Ok, I think your current issue is related to the default settings for the jQuery.ajax method. By default, the content type for the jQuery.ajax() method is 'application/x-www-form-urlencoded; charset=UTF-8'. So, in a sample project, I modified your javascript function to specify the contentType as a paramter to the ajax method: contentType: this.enctype

I think there also may be a couple additional issues. The next issue that I noticed is that on submission, I was connecting to another actions, so I updated this line in the view:

@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))

To this:

@using (Html.BeginForm("EditSlider", "<Your Controller Name>", FormMethod.Post, new { enctype = "multipart/form-data" }))

Lastly, when the ajax submitted, I was being redirected to the partial view. I believe this can be fixed by adding preventDefault to the ajax function:

$('form', dialog).submit(function (event) {
    event.preventDefault();
    var $form = $(this);
    $.ajax({
        url: this.action,
        type: this.method,
        contentType: this.enctype,
        data: $(this).serialize(),
        success: function (result) {
            if (result.success) {
                $('#myModal').modal('hide');
                //Refresh
                location.reload();
            } else {
                $('#myModalContent').html(result);
                bindForm();
            }
        }
    });
});

This is how I was able to get this example working in a sample project; please post an update if you have additional issues.

Jackhammer answered 3/11, 2014 at 17:56 Comment(0)
P
0

Try with following way this worked for me :

VIEW :

@using (Html.BeginForm("ComplaintAndSuggestion", "RegisterComplaints", FormMethod.Post, new { enctype = "multipart/form-data", id = "ajaxUploadForm" }))
{
:
:
:

                    <div class="row mb10">
                        <div class="col-sm-3 col-md-3">
                            <label for="file1">Upload Image 1</label>
                            <input type="file" name="images" id="file1" accept="image/*" />
                        </div>
                        <div class="col-sm-3 col-md-3">
                            <label for="file2">Upload Image 2</label>
                            <input type="file" name="images" id="file2" accept="image/*" />
                        </div>
                        <div class="col-sm-3 col-md-3">
                            <label for="file3">Upload Image 3</label>
                            <input type="file" name="images" id="file3" accept="image/*" />
                        </div>
                        <div class="col-sm-3 col-md-3">
                            <label for="file4">Upload Image 4</label>
                            <input type="file" name="images" id="file4" accept="image/*" />
                        </div>
                    </div>

   <input type="submit" value="Create" />

}

Controller :

     [HttpPost]
        public ActionResult ComplaintAndSuggestion(Register register, IEnumerable<HttpPostedFileBase> images, IEnumerable<HttpPostedFileBase> videos)
        {


 foreach (var file in images)
                {

                    if (file != null)
                    {
                        string filenameWithDateTime = AppendTimeStamp(file.FileName);
                        file.SaveAs(Server.MapPath(Path.Combine("~/Images/", filenameWithDateTime)));
                        fileUploadModel.FilePath = (Server.MapPath(Path.Combine("~/Images/", filenameWithDateTime)));
                        fileUploadModel.FileName = filenameWithDateTime;
                        fileUploadModel.FileType = "Image";
                        fileUploadModel.RegisterId = register.RegisterId;
                        mediaRepository.Add(fileUploadModel);
                        mediaRepository.Save();
                    }

                }

}

Let me know.

Pinkeye answered 25/10, 2014 at 15:55 Comment(1)
This might work for you but can you find whats wrong in my code @GaneshRunthrough
R
0

Usually, modal pop-ups are rendered at the end of the body. I'm pretty sure that bootstrap does the same thing. This, in turn, means that the content of the modal is moved to a new location and is taken out of your form element. I would recommend a reordering: move the form inside the modal window:

<div class="modal-body">
    @using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
    {
        @Html.AntiForgeryToken()
        ...
    }
</div>

This will move the entire form (and not just the form elements) when the modal gets built.

Regen answered 28/10, 2014 at 7:36 Comment(0)
F
0

Only the file uploaded return null? or the other textbox return null as well? As @Andrei comments, usually the modals is moved to a new location and the form is not present or your partialview could be moved to make the modal works.

With jqueryUI I had a similar problem. I couldn't find in your question if you are using ajax to submit your data, you could use .ajax() to send your form and see if the image was upload

$.ajax({
            url: "@Url.Action("Action", "Controller")",
            type: "POST",
            cache: false,
            data: $("#YourFormID").serialize(),
            success: function (result) {
               //your success data

            },
            error: function (jqXHR, textStatus, errorThrown) {
               //your error handler
            },
        });

         return false;

If you don't want to use $.ajax(), you could try to use .appendTo() with jquery, wrap everything what it is inside your form in a div with an ID, then after you have all your data try to sepecify that you want to appendTo("#YourFormID") on a button click, or as you like. This work for me when I was using modal, hope it helps you with something. Good luck

Flotage answered 28/10, 2014 at 16:17 Comment(0)
I
0

If I understand you make the form in a partial View, and this partial is used in a modal popup, it's right.

1) make a model for used in the form with all elements for the form, 2) declare the model in the first line in the partial view 3) pass as parameter the model to the post function. 4) you use a Partial view, well is possible use this view in differents pages, you need specify the control to treatement the form. in code:

MODEL

public partial class SliderModel
{
    [Required]
    [StringLength(200)]
    public string FileName { get; set; }

    [StringLength(200)]
    public string Title { get; set; }

    [StringLength(1000)]
    public string Description { get; set; }

    public int? Order { get; set; }

    [NotMapped]
    public HttpPostedFileBase ImageFile { get; set; }
}

VIEW

@model YOURNAMESPACE.Models.SliderModel
<form method="post" class="form-horizontal" role="form" id="sendMail" 
    enctype="multipart/form-data"  action="/CONTROLLERNAME/EditSlider">

@Html.AntiForgeryToken()
<div class="modal-body">
    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.Id)

        <div class="form-group">
            @Html.LabelFor(model => model.FileName, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(model => model.FileName, new { @class = "form-control", @readonly = "readonly" })
                @Html.ValidationMessageFor(model => model.FileName, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(model => model.ImageFile, new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.ImageFile, new { @class = "form-control", type = "file" })
               //This is Same as below
               //<input class="form-control" id="ImageFile" name="ImageFile" type="file" value="">
            </div>
        </div>
<div class="form-group">
        <div class="col-md-offset-1">
            <button type="submit" class="btn btn-success"><b><i class="fa fa-envelope"></i> Envoyer</b> </button>
        </div>
    </div>
</form>

CONTROLLER

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditSlider(SliderModel obj)
{
    if (ModelState.IsValid)
    {
        your options
    }
    return PartialView("_EditSlider");
}
Innocuous answered 1/11, 2014 at 6:52 Comment(2)
Isn't this the same what I'm doing @Pablote ??Runthrough
you make two models: public partial class Slider : BaseModel and class SliderImage : Slider, i make only one, slidermodel; in view I writre the entire partial view, is needed write @model and action="/YOURCONTROLLERNAME/EditSlider" inside the partial view. the parameter in the controller is (SliderModel obj) well this litle differences are the problemInnocuous

© 2022 - 2024 — McMap. All rights reserved.