简体   繁体   中英

ASP.Net MVC Upload file using jquery-form plugin and returning a Json result

I'm trying to use the JQuery Form plugin (http://jquery.malsup.com/form/) to upload a file and a couple extra fields from my view, and I want the action method to return a Json result to the javascript callback.

Currently, the ActionMethod is called correctly (I can process the files and fields from the form) but when I return the Json result the browser tries to download it as a file (If I download the file and see its contents, it's the JSON content that I am returning.).

This is my form:

<form id="FormNewFile" action="@Url.Content("~/Home/AddFile")" method="post" enctype="multipart/form-data">
    <input type="hidden" name="eventId" value="25" />
    <input type="text" name="description" />
    <input type="file" name="fileName" />
    <input type="submit" value="Send!" />
</form>

This is my javascript:

  <script type="text/javascript">
     $(function () {
        $("#FormNewFile").ajaxForm({
           dataType:'json',
           success:processJson
     });
     });

     function processJson(a,b) {
        alert('success');
     }
  </script>

And this is my ActionMethod:

   [HttpPost]
   public ActionResult AddFile(long? eventId, string description)
   {
      int id = 5;
      return Json(new {id});
   }

The name of the file the browser tries to download is something like AddFilee87ce48e, with the last 8 characters being random hexadecimal characters.

And finally, the content of the file downloaded is:

{"id":5}

And the processJson function in javascript never gets called.

I googled a lot and the only thing that seems to work is returning the JSON result as a "Content" result from the action method, I think that's the approach I'm gonna take, but I still want to know why this isn't working?

Any ideas?

What I ended up doing was manually serializing the object I wanted to return as JSON, if I do this then the response won't have a header, therefore it will be handled by javascript instead of the browser. I used this helper method:

  public static ActionResult JsonPlain(object x)
  {
     var result = new ContentResult();
     result.Content = new JavaScriptSerializer().Serialize(x);
     return result;
  }

Hope this helps someone else

That's normal behavior. Excerpt from the documentation :

Since it is not possible to upload files using the browser's XMLHttpRequest object, the Form Plugin uses a hidden iframe element to help with the task. This is a common technique, but it has inherent limitations. The iframe element is used as the target of the form's submit operation which means that the server response is written to the iframe. This is fine if the response type is HTML or XML, but doesn't work as well if the response type is script or JSON, both of which often contain characters that need to be repesented using entity references when found in HTML markup.

To account for the challenges of script and JSON responses, the Form Plugin allows these responses to be embedded in a textarea element and it is recommended that you do so for these response types when used in conjuction with file uploads. Please note, however, that if there is no file input in the form then the request uses normal XHR to submit the form (not an iframe). This puts the burden on your server code to know when to use a textarea and when not to. If you like, you can use the iframe option of the plugin to force it to always use an iframe mode and then your server can always embed the response in a textarea. The following response shows how a script should be returned from the server:

This means that if you want to return JSON you need to wrap it in a <textarea> tags on your server. To achieve this you could write a custom action result deriving from JsonResult and wrapping the generated JSON inside those tags.

以下博客文章, 不使用Flash在ASP.NET MVC中使用jQuery File Upload ,解决了达林·迪米特洛夫(Darin Dimitrov)的回答所述将响应包装在文本区域中的问题。

Have a look at the action on your form tag:

action="@Url.Content("~/Home/AddFile")"

The only usage I've seen of this is in script tags such as :

<script language="javascript" src="<%=Url.Content("~/Scripts/jquery-1.3.2.js")%>

The correct reference for the action would be:

action="/Home/Addfile"

While I am not too sure about the explanation as to why but the UrlHelper:

Url.Content("~/somepath")

converts a virtual path to a absolute path and expects a physical file such as a javascript.

The json result might be being returned as a file download because the mvc framework is expecting a file download of some description. Just as:

<a href="<%= Url.Content("~/_docs/folder/folder/document.pdf") %>">
document.pdf</a>

would download or display a physical pdf file.

My solution using json and no specific javascript:

/// <summary>
/// A result to use in combination with jquery.ajax.form when we want to upload a file using ajax
/// </summary>
public class FileJsonResult : JsonResult
{
    public JsonResult Result { get; set; }

    public FileJsonResult(JsonResult result):base()
    {
        this.Result = result;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.Write("<textarea>");
        this.Result.ExecuteResult(context);
        context.HttpContext.Response.Write("</textarea>");
        context.HttpContext.Response.ContentType = "text/html";
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM