简体   繁体   中英

Handling ASP.NET MVC FileResult returned

In my controller I have an ActionResult which returns a File.

[HttpPost]
public ActionResult ExportCSV(ReportResultViewModel model)   
{     
    var content = "hello,world";
    return File(Encoding.UTF8.GetBytes(content),"text/csv","export.csv");
}

In my view, when I post to this ActionResult, I display a modal saying "please wait".

<!--modal-->
<div class="modal fade" id="pleaseWaitDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content" style="background: #EBF3EB;">
            <div class="modal-header">
                <h1>Please wait...</h1>
            </div>
            <div class="modal-body">
                <div id="loader"></div>
            </div>
       </div>
    </div>
</div>
@using (Html.BeginForm("ExportCSV", "Reporting", FormMethod.Post, new { name = "back", id = "back", style = "width:100%" }))
{
     @Html.HiddenFor(m => m.A)
     @Html.HiddenFor(m => m.LOT)
     @Html.HiddenFor(m => m.OF)
     @Html.HiddenFor(m => m.THINGS)

     <input type="submit" data-toggle="modal" data-target="#pleaseWaitDialog" value="Export CSV" style="width: 100%; background: #fff" class="btn btn-default"  />
}

I want to hide it when the file is finally returned to the page.

Is there a way to detect client side (with JavaScript maybe) when the file arrives so I can hide the modal?

I think what you are after is the jQuery File Download http://jqueryfiledownload.apphb.com/ in your view add a reference to the jquery ui library and file download library then add a script tag.

<script type="text/javascript">

$(function () {
    var $pleaseWaitDialog= $("#pleaseWaitDialog"); 

    $(document).on("submit", "#back", function (e) {

        $pleaseWaitDialog.dialog({ modal: true });

        $.fileDownload($(this).prop('action'), {
            httpMethod: 'POST',
            data: $(this).serialize,
            successCallback: function (url) {
                $pleaseWaitDialog.dialog('close');
            },
            failCallback: function (responseHtml, url) {
                $pleaseWaitDialog.dialog('close');
            }
        });
        e.preventDefault; //otherwise normal form submit will occur 
    });
});
</script>

What this will do is when the submit button is clicked for the #ExportCSV form it will show a modal dialog box for the #pleaseWaitDialog tag. Then using the fileDownload plugin it will fire a post to the action url of the form. The data submitted comes from the $(this).serialize call. When the file has successfully been downloaded or if the call failed it simply closes the dialog box.

If you want to capture the return event, you'll have to submit the original request yourself over AJAX. Which means JavaScript will have to be ultimately responsible for downloading the file, instead of the browser via the Content-Disposition: attachment header .

In your controller, instead of returning a FileActionResult , you can convert the file into a Base64String and return that like this:

Byte[] bytes = File.ReadAllBytes("path");

if (Request.IsAjaxRequest())
{
    var file64 = Convert.ToBase64String(bytes);
    return Json( new {File = file64, MimeType = mimeType, FileName = fileName});
}
else
{
    // return file - should just auto download
    return File(bytes, mimeType, fileName);
}

Then in your AJAX handler, you can use HTML5/Javascript to generate and save a file using base64 encoding like this:

$(":submit").on("click", function(e) {
    e.preventDefault();
    var $form = $(this).closest('form');

    // disable buttons
    var $btns = $(":submit").addClass("disabled");

    $.ajax({
        type: $form.attr("method"),
        url: $form.attr("action"),
        data: $form.serializeArray(),
        success: function (data) {

            var pom = document.createElement('a');
            pom.setAttribute('href', 'data:' + data.MimeType + ';base64,' + data.File);
            pom.setAttribute('download', data.FileName);

            if (document.createEvent) {
                var event = document.createEvent('MouseEvents');
                event.initEvent('click', true, true);
                pom.dispatchEvent(event);
            }
            else {
                pom.click();
            }

            // reset buttons
            $btns.removeClass("disabled");
        },
        error: function (jqXHR, textStatus, errorThrown) {
            console.log(jqXHR);
        }
    });
});

IE / Edge Support

For IE /Edge , you'll need to use window.navigator.msSaveBlob . If you're sending back a base64 string, you'll also need to Creating a Blob from a base64 string in JavaScript

Further Reading :

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