0

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

            }); 
6
  • What's returned has nothing to do with ServiceStack. It all boils down to your return File line. However, including the filename parameter should set the Content-Disposition to attachment (i.e. a download), so there's nothing wrong with the code you have currently. Given that you have commented return that would actually return inline, are you sure that you have rebuilt your solution since you made this change? Commented Jul 20, 2017 at 16:10
  • Yes, I have rebuilt and see the same results. Commented Jul 20, 2017 at 16:15
  • Why download the link via AJAX request? AJAX requests are known to be behave for downloading files. Just use normal @Html.ActionLink() - your page will not reload if all you do is file download. Commented Jul 20, 2017 at 18:05
  • Is it possible to fire that inside of a click event. I have some functions that are triggered at the same time to prepare a request. How would I do that and then hit the controller. Commented Jul 20, 2017 at 18:10
  • Went ahead and kept this change as I like it better, it produces the same result however. Commented Jul 20, 2017 at 19:19

1 Answer 1

1

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>
Sign up to request clarification or add additional context in comments.

3 Comments

I like this and may repurpose, the issue does seem to be ajax not handling the response very well...
Got this to work for me. I simply just used your success function and substituted my response for converted as mine was already in a string. Ill add the solution to my original question Thanks.
Glad to help :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.