将使用OpenXML创建的Excel电子表格导出到客户端

3

我正在尝试调用API来启动已创建的Excel电子表格的下载。不过,似乎遇到了一些麻烦。我也尝试将电子表格内存流的字节数组发送到前端并从那里开始处理,但是Excel文件损坏且不包含任何数据。

控制器:

    [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类带有创建电子表格的方法:

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

编辑

前端服务:

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

我知道映射器不必存在,但如果需要将字节数组带到前端并在那里进行操作,我会保留它。

如有任何疑问或指导,将不胜感激。

谢谢。

对于那些感兴趣或遇到类似问题的人已找到解决方案(请参见下面的作者答案)

我在TypeScript中的服务调用中添加了{ responseType: ResponseContentType.Blob }

然后它返回给我一个电子表格的blob。从那里,在typescript中我通过另一种方法运行它:

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

为了让我的文件在客户端下载,感谢所有人,特别是答案的作者。

你们使用的前端框架是什么?Angular?React?还是其他的? - Siavash
1
@SiavashGhanbari 目前使用的是Angular和Typescript。 - fluffy
1
我在这里回答了答案 https://dev59.com/T67la4cB1Zd3GeqPkuSV#52667543 ,如果还有问题,请告诉我。 - Siavash
2个回答

4

您需要告诉Angular,该响应不是JSON格式,因此它不会尝试解析它。请尝试将您的代码更改为:

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

上面的代码是针对二进制格式的,但是对于Excel文件,你应该使用以下代码:

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

我简直想亲吻你。在第一个代码块中添加响应类型后,它返回了一个二进制大对象。如果您感兴趣,请查看处理后的编辑代码。 - fluffy

0
你的 "return ms.ToArray();" 行需要移动到 using 里面,可能还需要加上 "document.Close();":
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
            ...

            //Styling the doucment
            ...

            //Adding width to the columns
            ...

            //Creating the worksheet part to add the data to
            ...

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

            //Creating the first Header Row
            ...

            //Appending the data into their respective columns 
            foreach (var ent in barBillExport)
            {
                ...
            }

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

尝试了一下,运气不太好。感谢你的尝试。 - fluffy
@sparemesomecode ms.ToArray() 已经在 using 块内部。如果不是的话,它会抛出一个 ObjectDisposedException 异常。 - Panagiotis Kanavos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接