Как сделать сообщение формы ASP.NET MVC Ajax с данными multipart/form?

Я работаю над веб-сайтом ASP.NET MVC, который имеет форму, которая позволяет загружать файлы, используя параметр multipate/form data enctype в теге формы, например

<form enctype="multipart/form-data" method="post" action='<%= Url.Action("Post","Entries",new {id=ViewData.Model.MemberDetermination.DeterminationMemberID})  %>'>

Как я могу написать это, чтобы вместо сообщения формы ASP.NET MVC Ajax?

Ответ 1

  • Вы можете использовать некоторые дополнительные загрузчики (например, jQuery для загрузки нескольких файлов) (я предпочитаю этот путь, и я предпочитаю не использовать MS Ajax)
  • Применение:

    AjaxHelper.BeginForm("Post", "Entries", new {id=ViewData.Model.MemberDetermination.DeterminationMemberID}, new AjaxOptions(){/*some options*/}, new {enctype="multipart/form-data"})
    

Но во втором случае я не уверен, что он сработает.

Ответ 2

Это возможно, но это долгий путь. Шаг 1: напишите свою форму

Пример:

@using (Ajax.BeginForm(YourMethod, YourController, new { id= Model.Id }, new AjaxOptions {//needed options }, new { enctype = "multipart/form-data" }))
{
    <input type="file" id="image" name="image" />
    <input type="submit" value="Modify" />
}

Шаг 2: перехватите запрос и отправьте его на сервер

<script type="text/javascript">
    $(function() {
        $("#form0").submit(function(event) {
            var dataString;
            event.preventDefault();
            var action = $("#form0").attr("action");
            if ($("#form0").attr("enctype") == "multipart/form-data") {
                //this only works in some browsers.
                //purpose? to submit files over ajax. because screw iframes.
                //also, we need to call .get(0) on the jQuery element to turn it into a regular DOM element so that FormData can use it.
                dataString = new FormData($("#form0").get(0));
                contentType = false;
                processData = false;
            } else {
                // regular form, do your own thing if you need it
            }
            $.ajax({
                type: "POST",
                url: action,
                data: dataString,
                dataType: "json", //change to your own, else read my note above on enabling the JsonValueProviderFactory in MVC
                contentType: contentType,
                processData: processData,
                success: function(data) {
                    //BTW, data is one of the worst names you can make for a variable
                    //handleSuccessFunctionHERE(data);
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    //do your own thing
                    alert("fail");
                }
            });
        }); //end .submit()
    });
</script>

Шаг 3: Поскольку вы делаете вызов ajax, вы, вероятно, захотите заменить какое-либо изображение или что-то вроде multipart/form-data

Пример:

handleSuccessFunctionHERE(data)
{
    $.ajax({
        type: "GET",
        url: "/Profile/GetImageModified",
        data: {},
        dataType: "text",
        success: function (MSG) {
            $("#imageUploaded").attr("src", "data:image/gif;base64,"+msg);
        },
        error: function (msg) {
            alert(msg);
        }
    });
}

MSG-переменная - это зашифрованная строка base64. В моем случае это источник изображения.

Таким образом мне удалось изменить изображение профиля, после чего изображение сразу обновится. Также убедитесь, что вы добавили Application_Start (global.asax) ValueProviderFactories.Factories.Add(new JsonValueProviderFactory()); Довольно приятно нет?

P.S.: Это Решение работает, поэтому не стесняйтесь спрашивать более подробную информацию.

Ответ 3

Я наткнулся на этот маленький взлом, который прекрасно его разрешает

window.addEventListener("submit", function (e) {
    var form = e.target;
    if (form.getAttribute("enctype") === "multipart/form-data") {
        if (form.dataset.ajax) {
            e.preventDefault();
            e.stopImmediatePropagation();
            var xhr = new XMLHttpRequest();
            xhr.open(form.method, form.action);
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    if (form.dataset.ajaxUpdate) {
                        var updateTarget = document.querySelector(form.dataset.ajaxUpdate);
                        if (updateTarget) {
                            updateTarget.innerHTML = xhr.responseText;
                        } 
                    }
                }
            };
            xhr.send(new FormData(form));
        }
    }
}, true);

Ответ 5

Код, который я использовал, и он работает! Это копия решения @James 'Fluffy' Burton. Я просто импровизирую его ответ, чтобы люди, которые новичок в MVC, смогут быстро понять последствия.

Ниже представлен мой вид:

@using (Ajax.BeginForm("FileUploader", null, new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "AjaxUpdatePanel" }, new { enctype = "multipart/form-data", id = "frmUploader" })){
<div id="AjaxUpdatePanel">     
    <div class="form-group">
        <input type="file" id="dataFile" name="upload" />
    </div>

    <div class="form-group">
        <input type="submit" value="Upload" class="btn btn-default" id="btnUpload"/>
    </div>

</div>}

<script>
window.addEventListener("submit", function (e) {
    var form = e.target;
    if (form.getAttribute("enctype") === "multipart/form-data") {
        if (form.dataset.ajax) {
            e.preventDefault();
            e.stopImmediatePropagation();
            var xhr = new XMLHttpRequest();
            xhr.open(form.method, form.action);
            xhr.onreadystatechange = function () {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    if (form.dataset.ajaxUpdate) {
                        var updateTarget = document.querySelector(form.dataset.ajaxUpdate);
                        if (updateTarget) {
                            updateTarget.innerHTML = xhr.responseText;
                        }
                    }
                }
            };
            xhr.send(new FormData(form));
        }
    }
}, true);

Ниже приведен мой контроллер:

[HttpPost]
    public JsonResult FileUploader(HttpPostedFileBase upload)
    {
        if (ModelState.IsValid)
        {
            if (upload != null && upload.ContentLength > 0)
            {

                if (upload.FileName.EndsWith(".csv"))
                {
                    Stream stream = upload.InputStream;
                    DataTable csvTable = new DataTable();
                    using (CsvReader csvReader = new CsvReader(new StreamReader(stream), true))
                    {
                        csvTable.Load(csvReader);
                    }
                }
                else
                {
                    return Json(new { dataerror = true, errormsg = "This file format is not supported" });
                }
            }
            else
            {
                return Json(new { dataerror = true, errormsg = "Please Upload Your file" });
            }
        }
        return Json(new { result = true });
    }

Ниже приведена краткая заметка вышеприведенного кода: Через Ajax я разместил файл excel (*.csv) на сервере и прочитал его в DataTable с помощью пакета Nuget (LumenWorksCsvReader).

Ура! Оно работает. Спасибо @James

Ответ 6

Я сам сам ответил на вопрос...

<% using (Ajax.BeginForm("Post", "Entries", new { id = ViewData.Model.MemberDetermination.DeterminationMemberID }, new AjaxOptions { UpdateTargetId = "dc_goal_placeholder" }, new { enctype = "multipart/form-data" }))

Ответ 7

Для тех, у кого все еще есть проблемы с использованием @Ajax.BeginForm для многопользовательских enctypes/загрузки файлов в MVC

Диагностика и предлагаемое решение

Запуск инструмента "Осмотреть элемент" в элементе формы, сгенерированном помощником @Ajax.BeginForm, показывает, что помощник, довольно необъяснимо, переопределяет указанный параметр контроллера. Это тот случай, если вы внедрили отдельный контроллер для частичной обратной передачи.

Быстрое исправление проблемы заключается в том, чтобы явно указать значение атрибута действия html как /<yourcontrollername>/<youractionname>.

Пример

@using (Ajax.BeginForm("", "", new AjaxOptions() { HttpMethod = "POST", UpdateTargetId = "<TargetElementId>", InsertionMode = InsertionMode.Replace }, new { enctype = "multipart/form-data", action = "/<Controller>/<Action>" }))

Ответ 8

Ajax.BegineForm() работает с данными мультифайловой формы, и здесь пример рабочего кода для него:

Вид:

@using(Ajax.BeginForm("UploadFile","MyPOC",
        new AjaxOptions { 
            HttpMethod = "POST"
        }, 
        new 
        {
            enctype = "multipart/form-data"
        }))
    {
        <input type="file" name="files" id="fileUploaderControl" />
        <input type="submit" value="Upload" id="btnFileUpload" />
    }

Метод действия контроллера:

public void UploadFile(IEnumerable<HttpPostedFileBase> files)
    {
        HttpPostedFileBase file = files.FirstOrDefault(); //Attach a debugger here and check whether you are getting your file on server side or null.
        if (file != null && file.ContentLength > 0)
        {
            //Do other validations before saving the file

            //Save File
            file.SaveAs(path);
        }
    }

P.S. Убедитесь, что атрибут "name" элемента управления загрузчиком файла и имя параметра, переданного методу UploadFile(), должны быть одинаковыми (например, "файлы" в этом случае).

Ответ 9

Я смешал Брэда Ларсона с Амирхосейном Мехрварзи, потому что ответ Брэда не предоставлял никакого способа справиться с ответом, и Амирхосейн вызывал 2 обратной передачи. Я просто добавил ($ ('# formBacklink'). Valid()), чтобы вызвать проверку модели перед отправкой.

window.addEventListener("submit", function (e) {
        if ($('#formBacklink').valid()) {
            var form = e.target;
            if (form.getAttribute("enctype") === "multipart/form-data") {
                if (form.dataset.ajax) {
                    e.preventDefault();
                    e.stopImmediatePropagation();

                    var dataString;
                    event.preventDefault();
                    var action = $("#formBacklink").attr("action");
                    if ($("#formBacklink").attr("enctype") == "multipart/form-data") {
                        //this only works in some browsers.
                        //purpose? to submit files over ajax. because screw iframes.
                        //also, we need to call .get(0) on the jQuery element to turn it into a regular DOM element so that FormData can use it.
                        dataString = new FormData($("#formBacklink").get(0));
                        contentType = false;
                        processData = false;
                    } else {
                        // regular form, do your own thing if you need it
                    }
                    $.ajax({
                        type: "POST",
                        url: action,
                        data: dataString,
                        dataType: "json", //change to your own, else read my note above on enabling the JsonValueProviderFactory in MVC
                        contentType: contentType,
                        processData: processData,
                        success: function (data) {
                            //BTW, data is one of the worst names you can make for a variable
                            //handleSuccessFunctionHERE(data);   
                        },
                        error: function (jqXHR, textStatus, errorThrown) {
                            //do your own thing       
                        }
                    });
                }
            }
        }
    }, true);

Ответ 10

Если вам нужно использовать OnSuccess AjaxOption и/или использовать Request.IsAjaxRequest() в контроллере, чтобы проверить тип запроса i.e.

@using (Ajax.BeginForm("FileUploader", null, new AjaxOptions { HttpMethod = "POST", UpdateTargetId = "elementToUpdate", OnSuccess = "mySuccessFuntion(returnedData)", OnFailure = "myFailureFuntion(returnedData)"}, new { enctype = "multipart/form-data" }))

Затем вы можете использовать следующий код (я изменил ответ @James 'Fluffy' Burton). Это также преобразует текст ответа в объект JSON, если он может (вы можете опустить это, если хотите).

<script>
if(typeof window.FormData === 'undefined') {
    alert("This browser doesn't support HTML5 file uploads!");
}
window.addEventListener("submit", function (e) {
    var form = e.target;
    if (form.getAttribute("enctype") === "multipart/form-data") {
        if (form.dataset.ajax) {
            e.preventDefault();
            e.stopImmediatePropagation();
            var xhr = new XMLHttpRequest();
            xhr.open(form.method, form.action);
            xhr.setRequestHeader("x-Requested-With", "XMLHttpRequest"); // this allows 'Request.IsAjaxRequest()' to work in the controller code
            xhr.onreadystatechange = function () {
                if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
                    var returnedData; //this variable needs to be named the same as the parameter in the function call specified for the AjaxOptions.OnSuccess
                    try {
                        returnedData = JSON.parse(xhr.responseText); //I also want my returned data to be parsed if it is a JSON object
                    }catch(e){
                        returnedData = xhr.responseText;
                    }
                    if (form.dataset.ajaxSuccess) {
                        eval(form.dataset.ajaxSuccess); //converts function text to real function and executes (not very safe though)
                    }
                    else if (form.dataset.ajaxFailure) {
                        eval(form.dataset.ajaxFailure);
                    }
                    if (form.dataset.ajaxUpdate) {
                        var updateTarget = document.querySelector(form.dataset.ajaxUpdate);
                        if (updateTarget) {
                            updateTarget.innerHTML = data;
                        }
                    }
                }
            };
            xhr.send(new FormData(form));
        }
    }
}, true);
</script>

N.B. Я использую функцию javascript eval() для преобразования строки в функцию... если у кого-то есть лучшее решение, прокомментируйте. Я также использую JQuery JSON.parse(), поэтому это не решение ванильного javascript, но для функции script не требуется, чтобы его можно было удалить.

Ответ 11

Из моего небольшого расследования. Все приведенные выше ответы кажутся правильными в зависимости от проблемы, имеющейся у Ajax.BeginForm. Тем не менее, я просто заметил, что проблема связана с библиотекой javascript ~/Scripts/jquery.unobtrusive-ajax.min.js в некотором случае. Поэтому в моем случае я просто удалил его из модели представления и решил использовать плагин JQuery Form для моей необходимой необходимости вместе с HTML-формой. Это было предложено выше.