20

I'm trying to send a 9MB .xls file as a response from web api controller method. The user will click a button on the page and this will trigger the download via the browser.

Here's what I've got so far but it doesn't work however it doesn't throw any exceptions either.

[AcceptVerbs("GET")]
public HttpResponseMessage ExportXls()
{
    try
    {
        byte[] excelData = m_toolsService.ExportToExcelFile();

        HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
        var stream = new MemoryStream(excelData);
        result.Content = new StreamContent(stream);
        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        {
            FileName = "Data.xls"
        };
        return result;
    }
    catch (Exception ex)
    {
        m_logger.ErrorException("Exception exporting as excel file: ", ex);
        return Request.CreateResponse(HttpStatusCode.InternalServerError);
    }
}

Here is the coffeescript/javascript jquery ajax call from a button click in the interface.

$.ajax(
    url: route
    dataType: 'json'
    type: 'GET'
    success: successCallback
    error: errorCallback 
    )

Now that I think about it perhaps the dataType is wrong and shouldn't be json...

Neil
  • 5,179
  • 8
  • 48
  • 87
  • 3
    How do you call your method? What does it mean does not work? Have you checked in fiddler/firebug/devconsole how the response look like? – nemesv Feb 12 '13 at 11:50
  • is it necessary to return HttpResponseMessage , can you not use stream? – CodeGuru Feb 12 '13 at 12:34
  • Are you trying to send the excel file to the page so that the user can download it? – Robert Feb 12 '13 at 13:21

6 Answers6

16

Works also as a HTTP GET method, but don't use $ajax, instead use window.open(url);

C# code:

    [HttpGet]
    [Route("report/{scheduleId:int}")]
    public HttpResponseMessage DownloadReport(int scheduleId)
    {
        var reportStream = GenerateExcelReport(scheduleId);
        var result = Request.CreateResponse(HttpStatusCode.OK);

        result.Content = new StreamContent(reportStream);
        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        {
            FileName = "Schedule Report.xlsx"
        };

        return result;
    }

JS code:

downloadScheduleReport: function (scheduleId) {
    var url = baseUrl + 'api/Tracker/report/' + scheduleId;
    window.open(url);
}
Leo
  • 398
  • 2
  • 8
13

I had to make a couple of small changes to get this to work

First: Change the method to a post

[AcceptVerbs("POST")]

Second: Change from using the jQuery ajax lib to use a hidden form, here's my service function for doing the hidden form and submitting it.

exportExcel: (successCallback) =>
    if $('#hidden-excel-form').length < 1
        $('<form>').attr(
            method: 'POST',
            id: 'hidden-excel-form',
            action: 'api/tools/exportXls'
        ).appendTo('body');

    $('#hidden-excel-form').bind("submit", successCallback)
    $('#hidden-excel-form').submit()

Hopefully there's a better way to do this but for the time being it's working and downloading the excel file nicely.

Neil
  • 5,179
  • 8
  • 48
  • 87
  • In your Web API code, shouldn't the MediaTypeHeaderValue be application/vnd.ms-excel? – Sudhanshu Mishra Mar 13 '14 at 12:11
  • mishrsud.. i tried that.. when you make a call from javascript.. it doesnt matter what media type it is, javascript simply doesn't know what to do with it. You must tell the browser you're accepting the file, and the browser will know if its something it can download or not. I tried various types of formatting, and it doesn't work until you explicitly submit or change window.location to point to the file url. – sksallaj Mar 15 '14 at 23:55
1

I experienced the same problem. Problem solved with the following:

window.open(url)

Rahul Sharma
  • 2,867
  • 2
  • 27
  • 40
Seher Acar
  • 11
  • 1
1

It will store the excel file created in a folder in the system and once its sent to Browser, it will be deleted .

     //path to store Excel file temporarily
     string tempPathExcelFile = AppDomain.CurrentDomain.BaseDirectory + DateTime.Now.Hour + DateTime.Now.Minute +
                          DateTime.Now.Second + DateTime.Now.Millisecond +
                          "_temp";
        try
        {
            //Get Excel using  Microsoft.Office.Interop.Excel;
            Excel.Workbook workbook = ExportDataSetToExcel();
            workbook.SaveAs(tempPathExcelFile, workbook.FileFormat);
            tempPathExcelFile = workbook.FullName;
            workbook.Close();
            byte[] fileBook = File.ReadAllBytes(tempPathExcelFile);
            MemoryStream stream = new MemoryStream();
            string excelBase64String = Convert.ToBase64String(fileBook);
            StreamWriter excelWriter = new StreamWriter(stream);
            excelWriter.Write(excelBase64String);
            excelWriter.Flush();
            stream.Position = 0;
            HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
            httpResponseMessage.Content = new StreamContent(stream);
            httpResponseMessage.Content.Headers.Add("x-filename", "ExcelReport.xlsx");
            httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.ms-excel");
            httpResponseMessage.Content.Headers.ContentDisposition =
                new ContentDispositionHeaderValue("attachment");
            httpResponseMessage.Content.Headers.ContentDisposition.FileName = "ExcelReport.xlsx";
            httpResponseMessage.StatusCode = HttpStatusCode.OK;
            return httpResponseMessage;

        }
        catch (Exception ex)
        {
            _logger.ErrorException(errorMessage, ex);
            return ReturnError(ErrorType.Error, errorMessage);
        }
        finally
        {
            if (File.Exists(tempPathExcelFile))
            {
                File.Delete(tempPathExcelFile);
            }
        }

      //Javascript Code
      $.ajax({
                    url:  "/api/exportReport",
                    type: 'GET',
                    headers: {
                        Accept: "application/vnd.ms-excel; base64",
                    },
                    success: function (data) {   
                        var uri = 'data:application/vnd.ms-excel;base64,' + data;
                        var link = document.createElement("a");    
                        link.href = uri;
                        link.style = "visibility:hidden";
                        link.download = "ExcelReport.xlsx";
                        document.body.appendChild(link);
                        link.click();
                        document.body.removeChild(link);                        
                    },
                    error: function () {
                        console.log('error Occured while Downloading CSV file.');
                    },
                }); 
    In the end create an empty anchor tag at the end of your html file. <a></a>
0

For.NetCore you can simply Return type as File.

public IActionResult ExportAsExcel()
        {
            try
            {
                using (var ms = new MemoryStream())
                {
                    var data = ExportData(); // Note: This Should be DataTable
                    data.ConvertAsStream(ms);
                    ms.Position = 0;
                    return File(ms.ToArray(), "application/octet-stream", "ExcelReport.xlsx");
                }
               
               
            }
            catch (Exception e)
            {
                _logger.LogError(e.Message, e);
                throw;
            }
        }
Karunakaran
  • 385
  • 5
  • 16
-3

This will return a file when you click a particular button

 public FileResult ExportXls(){
     //the byte stream is the file you want to return
     return File(bytes, "application/msexcel")
     }
Robert
  • 4,306
  • 11
  • 45
  • 95