LCaraway
LCaraway

Reputation: 1357

MVC Controller Returns CSV but does not Download Using AJAX

Inside my controller I am using ServiceStack.Text to serialize a List and wish to return it to the client.

I am passing json that gets passed and manipulated into a list of objects that I then want to return as a csv. I can confirm this part is working. The client reports the request was successful but no file is available for download. if i console.log the response it prints the csv string.

I actually receive a list of json strings that I deserialize and append to a single list.Again, I can confirm this is working as expected.

Here is the Code:

[ValidateInput(false)]
[HttpPost]
public FileContentResult DownloadCSV(List<string> json, string delimiter, string method)
    {
        var respCSV ="";

        if (method == "combine")
        {
            List<AgmtToCSV> comb = new List<AgmtToCSV>();

            foreach (var i in json)
            {
            var d = Newtonsoft.Json.JsonConvert.DeserializeObject<List<AgmtToCSV>>(i);
                foreach (var u in d)
                {
                    comb.Add(u);
                }

            }

            var csv = CsvSerializer.SerializeToCsv(comb);


            respCSV = csv;
        }



        return File(new System.Text.UTF8Encoding().GetBytes(respCSV), "text/csv", "ExpirationReport.csv");
            //return File(respCSV);
        }

EDIT

Here is what the response look like:

Cache-Control:private
Content-Disposition:attachment; filename=ExpirationReport.csv
Content-Encoding:gzip
Content-Length:3117
Content-Type:text/csv
Date:Thu, 20 Jul 2017 17:42:16 GMT
Server:Microsoft-IIS/8.0
Vary:Accept-Encoding
X-AspNet-Version:4.0.30319
X-AspNetMvc-Version:5.2
X-Powered-By:ASP.NET
X-SourceFiles:=?UTF-8?B?SDpcZGV2ZWxvcG1lbnRcQWdyZWVtZW50LVZpZXdlclxBViAxLjEuMyBkZXZcQVZcQVZcSG9tZVxEb3dubG9hZENTVg==?=

Here is the jquery request and how it handles the successful response..

$.post('@Url.Action("DownloadCSV", "Home")', { "json":dta, "delimiter":del, "method":"combine"}, function (r) {
    console.log("Success");
    console.log(r);

});

UPDATE - Solution

Was able to get this to work with the accepted answer: The success function was the key here. It creates a link that points to the download and then initiates it.

$.post('@Url.Action("DownloadCSV", "Home")', { "json":dta, "delimiter":del, "method":"combine"}, function (r) {
   console.log("Success");
   var link = document.createElement("a");
   link.id = "lnkDwnldLnk";
   var converted = r;

   document.body.appendChild(link);
   var csv = converted;
   blob = new Blob([csv], { type: 'text/csv' });
   window.URL = window.URL || window.webkitURL;
   var csvUrl = window.URL.createObjectURL(blob);
   var filename = 'ExpirationReport.csv';
   $("#lnkDwnldLnk")
        .attr({
        'download': filename,
        'href': csvUrl
        });
    var ua = window.navigator.userAgent;
    var msie = ua.indexOf("MSIE ");

                if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./))  // If Internet Explorer, return version number
                {
                    window.navigator.msSaveOrOpenBlob(blob, filename);
                }
                else {
                    $('#lnkDwnldLnk')[0].click();
                }


                document.body.removeChild(link);

            }); 

Upvotes: 0

Views: 1674

Answers (1)

Helpha
Helpha

Reputation: 481

I'm not exactly sure that my answer will do exactly as you want but still. I have this code that I'm using that basically takes in a JSON object, transforms it in a CSV and downloads it in the browser.

The controller code that goes to a service to get a list of objects and returns it as JSON.

        public ActionResult DownLoadExcel(long BatchNumber)
    {
        Context context = new Context();
        IQueryable<string> ICodeList = context.Codes.OrderByDescending(c => c.CreatedDateTime).Where(c => c.BatchNumber == BatchNumber).Select(c => c.Id); ;
        var codeList = ICodeList.ToList();


        return Json(codeList, JsonRequestBehavior.AllowGet);
    }

The AJAX call that gets the JSON list from the controller and transforms it into a CSV. Then it creates and fake a anchor and simulates a click on it to trigger the download.:

    <script type="text/javascript">
    function getExcel(batchNumber)
    {
        $.ajax({
            type: 'GET',
            url: '@Url.Action("DownloadExcel", "BatchCode")',
            data: { BatchNumber: batchNumber },
            cache: false,
            success: function (result) {
                var converted = ConvertToCSV(result);
                //this trick will generate a temp "a" tag
                var link = document.createElement("a");
                link.id = "lnkDwnldLnk";

                //this part will append the anchor tag and remove it after automatic click
                document.body.appendChild(link);
                var csv = converted;
                blob = new Blob([csv], { type: 'text/csv' });
                window.URL = window.URL || window.webkitURL;
                var csvUrl = window.URL.createObjectURL(blob);
                var filename = 'file.csv';
                $("#lnkDwnldLnk")
                .attr({
                    'download': filename,
                    'href': csvUrl
                });
                var ua = window.navigator.userAgent;
                var msie = ua.indexOf("MSIE ");

                if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./))  // If Internet Explorer, return version number
                {
                    window.navigator.msSaveOrOpenBlob(blob, filename);
                }
                else{
                    $('#lnkDwnldLnk')[0].click();
                }


                document.body.removeChild(link);
            }
        });
    }


function ConvertToCSV(objArray) {
    var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray;
    var str = '';

    for (var i = 0; i < array.length; i++) {
        var line = array[i];
        str += line + '\r\n';
    }

    return str;
}
</script>

And finally you need and empty a in your HTML

<a hidden="hidden"></a>

Upvotes: 1

Related Questions