3

I have 2 file upload for editing user profile in partial view that called from user profile page. The problem is HttpPostedFileBase parameter in action method always null.

When I'm calling this partial view out of profile page and without layout, file uploads was successfully done and sent files to action method.

This is my action method in controller:

[AcceptVerbs(HttpVerbs.Post)]
[Authorize(Roles = "User")]
public ActionResult EditProfile(ProfileGeneralDescription editedModel,
                                   HttpPostedFileBase imageFile,
                                   HttpPostedFileBase coverFile)
{
         //Some code here...   
}

And this is my partial view cshtml code:

@model Website.Models.ViewModel.Profile

@using (Ajax.BeginForm("EditProfile", "Profile", new { postOwnerUser = User.Identity.Name }, new AjaxOptions()
{
    HttpMethod = "POST",
    InsertionMode = InsertionMode.Replace,
    UpdateTargetId = "GeneralSection"
}, new { enctype = "multipart/form-data" }))
{

    <div>
        <button type="submit" name="Save" class="btn btn-default btn-xs">Save Changes</button>
        @Ajax.ActionLink("Cancel", "ProfileDescription", "Profile",
            new {username = Model.Username, type = "Show"},
            new AjaxOptions()
            {
                HttpMethod = "GET",
                InsertionMode = InsertionMode.Replace,
                UpdateTargetId = "GeneralSection"
            },
            new {@class = "btn btn-default btn-xs"})
    </div>

    <input type="hidden" name="username" id="username" value="@Model.Username"/>

    <fieldset>
        <legend>Edit Photos</legend>
        <div>
            Select profile picture:
            <input id="imageFile" type="file" name="imageFile" accept="image/png, image/jpeg" />
            @Html.CheckBoxFor(modelItem => modelItem.DefaultCover)<span>Remove profile photo</span>
        </div>
        <div>
            Select cover picture:
            <input id="coverFile" type="file" name="coverFile" accept="image/png, image/jpeg" />
            @Html.CheckBoxFor(modelItem => modelItem.DefaultCover)<span>RemoveCover</span>
        </div>
    </fieldset>
}

Where is my mistake?
Thanks in advance.

Mojtaba
  • 1,470
  • 4
  • 18
  • 25
  • 1
    Do you need the [HttpPost] attribute? – Dai Bok Dec 16 '13 at 11:12
  • I'm forget copy this codes. Please see my update. – Mojtaba Dec 16 '13 at 11:26
  • Can you give code showing how you are calling the partial view when it is not working? – Paul Taylor Dec 16 '13 at 11:43
  • This is my action link: `@Ajax.ActionLink("Edit Profile", "ProfileGeneralDescription", "Profile", new { username = Model.Username, type = "Edit" }, new AjaxOptions() { HttpMethod = "GET", InsertionMode = InsertionMode.Replace, UpdateTargetId = "GeneralSection" }, new { @class = "btn btn-default btn-xs" } )` – Mojtaba Dec 16 '13 at 11:46
  • I found this post: [mvc3 file upload using ajax form - Request.Files empty](http://stackoverflow.com/questions/8499748/mvc3-file-upload-using-ajax-form-request-files-empty) that best answer said Ajax form doesn't support file upload. What do I do to edit user profile? – Mojtaba Dec 16 '13 at 11:49
  • From my experience mojtaba is probably correct, typically "ajax" uploads are done with iframes to support older browsers, I have used jquery file uploader in the past – Dai Bok Dec 16 '13 at 11:56
  • thanks @DaiBok. Do you have any best practice and sample code to put file upload in update user profile? – Mojtaba Dec 16 '13 at 11:57
  • Check this question : http://stackoverflow.com/questions/9361789/sample-project-plugin-jquery-file-upload-plugin-implemented-in-asp-net-mvc3 – Dai Bok Dec 16 '13 at 12:12

1 Answers1

2

File uploads are normally initiated using a straightforward form post. If you want to initiate an upload using AJAX, you need to write some javascript to use the XmlHttpRequest (XHR) object, something like this (using jQuery):

<script>
    function submit (e){
        e.preventDefault = true;
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(e) {
            if ( 4 == this.readyState ) {
                console.log(['xhr upload complete', e]);
            }
        };
        xhr.open('post', 'Profile/EditProfile', true);
        xhr.setRequestHeader("Content-Type","multipart/form-data");
        var formData = new FormData();
        formData.append("imageFile", $("#imageFile").files[0]);
        formData.append("coverFile", $("#coverFile").files[0]);
        // etc
        xhr.send(formData);
    };
</script>

Note that you populate the javascript FormData object in code using the values from your form elements. Then wire this function to your submit button's click event:

    <button type="button" name="Save" onclick="submit()" class="btn btn-default btn-xs">Save Changes</button>

You can also remove the Ajax form in your view as it won't be needed when using this approach.

Paul Taylor
  • 5,651
  • 5
  • 44
  • 68
  • 1
    That's nice job! Thanks @Paul :-) – Mojtaba Dec 16 '13 at 12:10
  • How can I replace returned partial view with this block? (like `AjaxOptions` in `Ajax.BeginForm()` ). Current Option in beginForm function doesn't work! – Mojtaba Dec 16 '13 at 12:45
  • Is any updated data returned by your controller method that needs to be displayed? – Paul Taylor Dec 16 '13 at 13:12
  • If you do, you can get the server response from the XHR object like this: xhr.responseText or xhr.responseXML. Your script will then need to apply it to the relevant html element. – Paul Taylor Dec 16 '13 at 13:18
  • Yes, I do this as you said. But profile page was gone and returned partial view showed in browser. this script don't replace anything and seems it's not working after post data. – Mojtaba Dec 16 '13 at 13:33
  • Make sure your submit button isn't submitting the whole page to the server. The button needs to be type="button", not type="submit". – Paul Taylor Dec 16 '13 at 13:46
  • button type is "button" but it submitting the whole page. I've called submit function from FireBug. It complete successfully and new partial content was replaced by current content. Why it's not working in form posting? – Mojtaba Dec 16 '13 at 14:00
  • Because when the whole page is submitted, there are no partial updates like there are with Ajax / javascript calls. You need to find out what is causing the whole page to post. Check out the http://api.jquery.com/event.preventDefault/ property on the event object. Setting it to true in the click event handler might fix it. – Paul Taylor Dec 16 '13 at 14:10
  • I'm add e.preventDefault(); to function(e). But it's steel doesn't work. Why this function run successfully done by firebug but by clicking on button it doesn't works? :-( – Mojtaba Dec 16 '13 at 15:01
  • JQuery doesn't have any function that give tag Id va replace all content of it by return responseText ? (Like @Ajax.ActionLink Option) – Mojtaba Dec 16 '13 at 15:07
  • I should put this code in function(e)? After `console.log(['xhr upload complete', e]);` line? – Mojtaba Dec 16 '13 at 15:20
  • Yes, put it there. Btw, you need to set the e.preventDefault = true on the main function (see edited code in my response above). – Paul Taylor Dec 16 '13 at 15:29
  • Thanks @Paul. But it's steel not working for me and all whole page was refreshed. :-( – Mojtaba Dec 17 '13 at 07:07
  • The reason the whole page is posting is not to do with the script (as you can call the submit() function directly and it works). Check the rest of the page markup and research what may be causing the page to post to eliminate the problem. – Paul Taylor Dec 17 '13 at 09:09
  • Thanks @Paul. I solved this problem by jquery ajax call but steel I have some problem. Please see [This question](http://stackoverflow.com/questions/20629105/ajax-fileupload-jquery-formdata-in-asp-net-mvc) if you can. :-) – Mojtaba Dec 17 '13 at 09:14