【问题标题】:How to do a ASP.NET MVC Ajax form post with multipart/form-data?如何使用 multipart/form-data 进行 ASP.NET MVC Ajax 表单发布?
【发布时间】:2010-10-09 13:31:33
【问题描述】:

我正在开发一个 ASP.NET MVC 网站,该网站有一个表单,允许使用表单标签上的 multipart/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 表单发布?

【问题讨论】:

    标签: asp.net html asp.net-mvc


    【解决方案1】:

    这是可能的,但还有很长的路要走。 第 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" />
    }
    

    第二步:拦截请求并发送到服务器

    <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.:此解决方案有效,因此请随时询问更多详细信息。

    【讨论】:

    • 您也可以将第一个调用设为假调用[例如:转到控制器中的 void 方法]。然后你在哪里看到:'var action = $("#form0").attr("action");' >你可以采取你真正想要的行动。
    • 从 razor 传递一个虚拟方法,然后从 javascript 将其提交给正确的方法。:)
    • 您好,您可以在我的回答中看到代码。我可以告诉你的是 html5 支持文件上传,如果你开发的应用程序只能在 html5 上运行,那么有一种更简单的方法。
    • 添加 event.stopPropagation();以防止它提交两次。
    • [HttpPost] public ActionResult YourAction(HttpPostedFileBase file) { /*在此处保存文件 */ return RedirectToAction(YourAction); }
    【解决方案2】:

    我遇到了这个小技巧,它很好地解决了它

    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);
    

    【讨论】:

    • 将其复制粘贴到一个文档中。准备就绪并立即生效
    • 我也一样。只需复制/粘贴此代码,它就可以很好地工作。我正在失去我的头发试图解决我的问题。谢谢
    • 这应该是选择的答案。
    • 在花了几个小时试图弄清楚一切之后,这个解决方案奏效了
    • 伟大的黑客!也适用于 MS Edge。这应该被标记为事实上的答案。
    【解决方案3】:
    1. 您可以使用一些额外的上传器(例如jQuery multiple file uploader)(我更喜欢这种方式,我不喜欢使用 MS Ajax)
    2. 用途:

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

    但在第二种情况下,我不确定它是否会起作用。

    【讨论】:

    • 实际上您的选项 2 有效..我在这里遇到了另一个类似的问题,但是使用普通的 beginform 而不是 ajax beginform 助手...使用新的 enctype 对象,所以我想我会尝试一下出来了,它奏效了..
    • 我一直在努力避免添加太多的 jquery,因为我遇到了一些兼容性问题......即某些插件导致其他插件损坏等
    • @dswatik--你能贴一个#2的例子吗?我已经尝试过了,但无法让它工作。
    • 是的,请 dswatik 发布一个示例,因为一切正常,但我在服务器上没有收到任何图像文件!
    • 是的,我也没有文件发送到服务器。
    【解决方案4】:

    【讨论】:

      【解决方案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) 文件发布到服务器,并使用 Nuget 包 (LumenWorksCsvReader) 将其读取到 DataTable。

      万岁!有用。谢谢@詹姆斯

      【讨论】:

      【解决方案6】:

      其实我自己回答了这个问题……

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

      【讨论】:

        【解决方案7】:

        对于在 MVC 中使用 @Ajax.BeginForm 进行多部分编码/文件上传仍有问题的用户

        诊断和建议的解决方案

        在由@Ajax.BeginForm 助手生成的表单元素上运行“检查元素”工具会发现该助手会莫名其妙地覆盖指定的控制器参数。如果您为部分回发实现了单独的控制器,就会出现这种情况。

        该问题的快速解决方法是将您的 html 操作属性值明确指定为 /&lt;yourcontrollername&gt;/&lt;youractionname&gt;

        示例

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

        【讨论】:

          【解决方案8】:

          如果您需要使用OnSuccess AjaxOption 和/或在控制器中使用Request.IsAjaxRequest() 来检查请求类型,即

          @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>
          

          注意我使用 javascript 函数 eval() 将字符串转换为函数...如果有人有更好的解决方案,请发表评论。 我也使用 JQuery JSON.parse(),所以这不是一个普通的 javascript 解决方案,但脚本不需要它来运行,所以它可以被删除。

          【讨论】:

            【解决方案9】:

            我将 Brad Larson 的回答与 Amirhossein Mehrvarzi 混合在一起,因为 Brad 的回答没有提供任何处理响应的方法,而 Amirhossein 导致了 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】:

              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);
                      }
                  }
              

              附:确保文件上传器控件的“名称”属性和传递给 Action 方法 UploadFile() 的参数名称必须相同(即本例中的“文件”)。

              【讨论】:

              • 我不明白这是如何工作的。这个人stackoverflow.com/a/19044689/751796 也提出了完全相同的建议,但对我不起作用。也许你有一个插件可以无缝地做到这一点,比如 jquery 表单插件?
              • 嘿 Mikayil,当您说它不适合您时,具体问题是什么。我的意思是控制器的方法永远不会被命中还是文件参数为空?第一种情况的可能解决方案(从未命中控制器方法),检查上传按钮的输入类型是否为提交。对于第二个(当控制器接收到文件参数为空时),我将在上面的答案中重申我的脚注,其中说文件上传器控件的名称属性应该与控制器端的参数名称相同。希望这会有所帮助。
              • 问题是第二种情况,name属性与参数名匹配
              • 能否分享一下您使用过文件上传器的查看代码。此外,我上面答案中的代码也直接取自工作应用程序。因此,您也可以将其与您的进行比较。
              • 这对某些人来说“有效”的可能原因是他们的 Ajax.BeginForm 表现得像 Html.BeginForm 而不是异步发布。这通常发生在您没有正确加载 jquery.unobtrusive-ajax.js 时。所以这不是一个正确的答案。
              【解决方案11】:

              根据我的小调查。根据 Ajax.BeginForm 遇到的问题,上述所有答案似乎都是正确的。但是,我刚刚观察到在某些情况下问题出在 ~/Scripts/jquery.unobtrusive-ajax.min.js javascript 库上。因此,在我的情况下,我只是将它从视图模型中删除,并决定使用 JQuery Form 插件来满足我的需求以及 HTML 表单。这已在上面提出。

              【讨论】:

                【解决方案12】:

                您可以使用此代码代替 eval

                  var body = "function(a){ " + form.dataset.ajaxSuccess + "(a) }";
                  var wrap = s => "{ return " + body + " };"
                  var func = new Function(wrap(body));
                  func.call(null).call(null, returnedData);
                

                【讨论】:

                  猜你喜欢
                  • 2013-06-07
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-08-05
                  • 1970-01-01
                  • 1970-01-01
                  • 2018-12-26
                  相关资源
                  最近更新 更多