Search code examples
c#.netexcelopenxml

Exporting OpenXML created Excel Spreadsheet to client side


I am trying to get the API to start a download of the Excel spreadsheet that has been created. Although, I seem to be running into some hassle. I have tried to also send the Byte array of the Spreadsheets memory stream through to the front-end and go from there, but the Excel file is corrupt and does not contain any data.

Controller:

[HttpPost]
[Route("CreateExcelDocument")]
public ActionResult CreateExcelDocument([FromBody] List<BarBillList> model)
{
    try
    {
        byte[] tmp;
        using (ExcelController ex = new ExcelController())
        {
            tmp = ex.createExcelSpreadsheet(barBillExport);
        }

        string fileName = "xxx.xlsx";
        return File(tmp, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
    }
    catch (Exception e)
    {
        return null;
    }
}

ExcelController class with the spreadsheet creation method:

 public byte[] createExcelSpreadsheet(List<BarBillList> barBillExport)
    {
        DateTime today = DateTime.Today;
        using (MemoryStream ms = new MemoryStream())
        {
            using (SpreadsheetDocument document = SpreadsheetDocument.Create(ms, SpreadsheetDocumentType.Workbook))
            {
                //Creating the initial document
                WorkbookPart workbookPart = document.AddWorkbookPart();
                workbookPart.Workbook = new Workbook();

                WorksheetPart worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
                worksheetPart.Worksheet = new Worksheet();

                workbookPart.Workbook.Save();

                //Styling the doucment
                WorkbookStylesPart stylesPart = workbookPart.AddNewPart<WorkbookStylesPart>();
                stylesPart.Stylesheet = GenerateStyleSheet();
                stylesPart.Stylesheet.Save();

                //Adding width to the columns
                DocumentFormat.OpenXml.Spreadsheet.Columns columns = new DocumentFormat.OpenXml.Spreadsheet.Columns();
                columns.Append(new DocumentFormat.OpenXml.Spreadsheet.Column() { Min = 1, Max = 6, Width = 20, CustomWidth = true });
                worksheetPart.Worksheet.Append(columns);

                //Creating the worksheet part to add the data to
                Sheets sheets = workbookPart.Workbook.AppendChild(new Sheets());
                Sheet sheet = new Sheet() { Id = workbookPart.GetIdOfPart(worksheetPart), SheetId = 1, Name = "BarBill" };
                sheets.Append(sheet);

                SheetData sheetData = worksheetPart.Worksheet.AppendChild(new SheetData());

                //Creating the first Header Row
                Row row = new Row();
                row.Append(
                    ConstructCell("Name", CellValues.String, true),
                    ConstructCell("Last Payment Date", CellValues.String, true),
                    ConstructCell("Last Payment Amount", CellValues.String, true),
                    ConstructCell("Current Balance", CellValues.String, true));

                sheetData.AppendChild(row);

                //Appending the data into their respective columns 
                foreach (var ent in barBillExport)
                {
                    row = new Row();

                    row.Append(
                        ConstructCell(ent.Name.ToString(), CellValues.String, false),
                        ConstructCell((ent.LastPaymentDate.ToString().Length > 0) ? ent.LastPaymentDate.ToString() : "", CellValues.String, false),
                        ConstructCell((ent.LastPayment.ToString().Length > 0) ? ent.LastPayment.ToString() : "", CellValues.String, false),
                        ConstructCell((ent.TotalBalance.ToString().Length > 0) ? ent.TotalBalance.ToString() : "", CellValues.String, false));
                    sheetData.AppendChild(row);
                }

                worksheetPart.Worksheet.Save();
            }
            return ms.ToArray();
        }
    }

EDIT

Front End Service:

    createExcelDocument(model: BillList[]): any {
    return this.http.post(this.getBarBillsUrl + "/CreateExcelDocument", model)
        .map(this.helper.extractData)
        .catch(this.helper.handleError);
}

I am aware that the mapper does not need to be there, but I'm keeping it there should I need to bring the byte array through to the front and go from there.

Any direction or guidance on the matter would be greatly appreciated.

Solution Found for those interested or facing a similar issue (Please see below answers for author)

I added the { responseType: ResponseContentType.Blob } to the service call in TypeScript.

It then returned me a blob of the spreadsheet. From there, within the typescript i ran it through another method:

    private saveAsBlob(data: any) {
    const year = this.today.getFullYear();
    const month = this.today.getMonth();
    const date = this.today.getDate();
    const dateString = year + '-' + month + '-' + date;

    const file = new File([data], 'BarBill ' + dateString + '.xlsx',
        { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });

    FileSaver.saveAs(file);
}

To then get my file to download client side.

Thanks very much to all. Especially the author of the answer.


Solution

  • You need to tell Angular that the response is not JSON format , and so it will not try to parse it. Try changing your code to:

      createExcelDocument(model: BillList[]): any {
        return this.http.post(this.getBarBillsUrl + "/CreateExcelDocument", 
                model,  { responseType: ResponseContentType.Blob })
            .map(this.helper.extractData)
            .catch(this.helper.handleError);
    }
    

    The above code for binary format, but for excel files you should use this below code:

    const httpOptions = {
          headers: new HttpHeaders({ 'responseType':  'ResponseContentType.Blob',
          'Content-Type':  'application/vnd.ms-excel'})};
    
      createExcelDocument(model: BillList[]): any {
        return this.http.post(this.getBarBillsUrl + "/CreateExcelDocument", 
                model, httpOptions )
            .map(this.helper.extractData)
            .catch(this.helper.handleError);
    }