views:

527

answers:

1

I have a controller method "SaveFile" that attempts to download a binary stream to client's PC using the FileContentResult. If I make the call as a post from the controller corresponding view everything works OK. I want to call the same controller method "SaveFile" from a jquery function via an .ajax call, when I do the function returns an error and the responseText is the contents of the file to be written.

What would I have to do to get the same behavior from a jQuery function as if the MVC framework handled the call via the submit button? Or an alternate solution would be to determine how can I catch the browser's download dialog close event.

+1  A: 

Here's a quick example I made up. This is the concept LukLed was talking about with calling SaveFile but don't return file contents via ajax and instead redirect to the download.

Here's the view code:

<script src="../../Scripts/jquery-1.3.2.min.js" type="text/javascript"></script>
<script type="text/javascript">
    $(function() {
        // hide form code here

        // upload to server
        $('#btnUpload').click(function() {
            $.ajax({
                type: 'POST',
                dataType: 'json',
                url: '<%= Url.Action("SaveFile", "Home") %>',
                success: function(fileId) {
                    window.location = '<%= Url.Action("DownloadFile", "Home") %>?fileId=' + fileId;
                },
                error: function() {
                    alert('An error occurred uploading data.');
                }
            });
        });
    });
</script>

<% using (Html.BeginForm()) { %>

    <div>Field 1: <%= Html.TextBox("field1") %></div>

    <div>Field 2: <%= Html.TextBox("field2") %></div>

    <div>Field 3: <%= Html.TextBox("field3") %></div>

    <button id="btnUpload" type="button">Upload</button>

<% } %>

Here's the controller code:

[HandleError]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public JsonResult SaveFile(string field1, string field2, string field3)
    {
        // save the data to the database or where ever
        int savedFileId = 1;

        // return the saved file id to the browser
        return Json(savedFileId);
    }

    public FileContentResult DownloadFile(int fileId)
    {
        // load file content from db or file system
        string fileContents = "field1,field2,field3";

        // convert to byte array
        // use a different encoding if needed
        var encoding = new System.Text.ASCIIEncoding();
        byte[] returnContent = encoding.GetBytes(fileContents);

        return File(returnContent, "application/CSV", "test.csv");
    }

    public ActionResult About()
    {
        return View();
    }
}
Nate Pinchot
Nate, thanks for providing such a detailed example. I appreciate it and also thanks LukLed for trying diligently to explain your solution. The point that wasn't sinking-in was that the first call prepared the data to be saved, and 2nd call does the download.
Rick
I've been there before banging my head against the wall saying WTF to myself trying to understand, no worries. Glad to help.
Nate Pinchot