2

I am trying to download file through ajax call in asp.net

my javascript:

var allData = dataSource.data();
    var query = new kendo.data.Query(allData);
    var data = query.filter(filters).data;
    var strAccountCodes = '';
    for (var i = 0; i < data.length; i++) {
        strAccountCodes += data[i].AccountCode + ",";
    }
$.ajax({
        url: '@Url.Action("GetHistoricalUsageApplicationFile", "HUProducts")',
        type: 'GET',
        data: { "accountCodes": strAccountCodes }
    });

my action method:

public ActionResult GetHistoricalUsageApplicationFile([DataSourceRequest]DataSourceRequest request, [FromBody] string accountCodes)
    {
        var HistoricalUsagesData = _enrollmentManagementRepository.GetHistoricalUsageApplicationFile(accountCodes);
        List<HistoricalUsageApplicationFileModel> HUApplications = _mapper.MapToNew<List<HistoricalUsageApplicationFileModel>>(HistoricalUsagesData);
        //var HistoricalUsageApplication = HUReport.ToDataSourceResult(request).Data;

        var output = new MemoryStream();
        var writer = new StreamWriter(output, Encoding.UTF8);

        writer.Write("CommodityCode,");
        writer.Write("CustomerTypeCode,");
        writer.Write("EnrollmentRequestId");
        writer.WriteLine();

        var list = HUApplications.ConvertToString();
        var single = list.Aggregate((x, y) => { return string.Concat(x, y); });

        writer.WriteAsync(single);
        writer.Flush();
        output.Position = 0;

        return File(output, System.Net.Mime.MediaTypeNames.Application.Octet, "Products.csv");
    }

code is executing without any errors but it's not downloading any file.

is that anything i am missing?

tt0206
  • 747
  • 3
  • 9
  • 24
  • What is `strAccountCodes`, is that server-side or client-side variable? Of course you cannot download file using AJAX, you need an `ActionLink` or normal redirect to `GetHistoricalUsageApplicationFile` action method. – Tetsuya Yamamoto Feb 27 '19 at 02:08
  • I have updated javascript code above. strAccountCodes is string which i am sending to GetHistoricalUsageApplicationFile action method. – tt0206 Feb 27 '19 at 02:14
  • @TetsuyaYamamoto can you explain how can i achieve this with ActionLink. – tt0206 Feb 27 '19 at 02:18
  • Based from your current code, I preferred using redirect rather than `ActionLink` - creating another controller which only serves file download is necessary. Then pass the stream result to `Session` or `TempData`. – Tetsuya Yamamoto Feb 27 '19 at 02:20

1 Answers1

8

You should know that AJAX call is not intended to download CSV file directly. Therefore, you can create a byte array from MemoryStream instance and store it inside Session or TempData variable, then return 'successful' state to enable redirect on AJAX success response:

public ActionResult GetHistoricalUsageApplicationFile([DataSourceRequest]DataSourceRequest request, [FromBody] string accountCodes)
{
    var HistoricalUsagesData = _enrollmentManagementRepository.GetHistoricalUsageApplicationFile(accountCodes);
    List<HistoricalUsageApplicationFileModel> HUApplications = _mapper.MapToNew<List<HistoricalUsageApplicationFileModel>>(HistoricalUsagesData);
    //var HistoricalUsageApplication = HUReport.ToDataSourceResult(request).Data;

    var output = new MemoryStream();
    var writer = new StreamWriter(output, Encoding.UTF8);

    writer.Write("CommodityCode,");
    writer.Write("CustomerTypeCode,");
    writer.Write("EnrollmentRequestId");
    writer.WriteLine();

    var list = HUApplications.ConvertToString();
    var single = list.Aggregate((x, y) => { return string.Concat(x, y); });

    writer.WriteAsync(single);
    writer.Flush();
    output.Position = 0;

    // creates byte array from stream
    TempData["Output"] = output.ToArray();

    // returns successful state
    return Json("Success", JsonRequestBehavior.AllowGet);
}

Second, create a controller action with GET method and pass stored byte array from Session or TempData into FileResult:

public ActionResult DownloadCSV()
{
    // retrieve byte array here
    var array = TempData["Output"] as byte[];
    if (array != null)
    {
        return File(array, System.Net.Mime.MediaTypeNames.Application.Octet, "Products.csv");
    }
    else
    {
        return new EmptyResult();
    }
} 

Finally, handle success response to include location.href which will redirect to controller returning FileResult to download CSV file:

$.ajax({
    url: '@Url.Action("GetHistoricalUsageApplicationFile", "HUProducts")',
    type: 'GET',
    data: { "accountCodes": strAccountCodes },
    success: function (result) {
        if (result == "Success") {
            location.href = '@Url.Action("DownloadCSV", "ControllerName")';
        }
    }
});

As an option, you could pass CSV file name as parameter from AJAX response using query string.

Related issue:

Creating a byte array from a stream

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61