Excel 使用 Angular 下载和 Spring 引导生成损坏的 xls 文件

Excel download using Angular and Spring Boot produces corrupt xls file

我正在尝试为我的 Spring 引导应用程序编写 xls 下载。为了使用 POI 生成文件 Iam。如果我直接从我的控制器下载文件而不像这样将它传递给前端:

    //Wrote this one just for testing if the file is already corrupt here. 
    FileOutputStream fos = new FileOutputStream("C:\dev\directDownload.xls");
    fos.write(byteArray);
    fos.flush();
    fos.close();
}

它工作正常,文件如下所示:

xls 从后端下载而不传递给 angular:

然而那不是我的目标。我打算将 Outputstream 传递到我的 angular 组件。组件从服务 class 调用函数。此 class 从控制器获取响应并将其传递回我的组件。在 UI 中打开下载对话框。问题是,下载的文件看起来像这样(不管是通过 excel 还是 open office 打开):

损坏的 xls:

我的Java控制器:

   @CrossOrigin(exposedHeaders = "Content-Disposition")
@RequestMapping(value = "/report/file", produces = "application/vnd.ms-excel;charset=UTF-8")
public void getReportFile(@RequestParam(name = "projectNumber") final String projectNumber,
                          @RequestParam(name = "month") final int month, @RequestParam(name = "year") final int year,
                          @RequestParam(name = "employee") final int employee,
                          @RequestParam(name = "tsKey") final String tsKey,
                          final HttpServletResponse response) throws IOException {


    response.setContentType("application/vnd.ms-excel;charset=UTF-8");
    String excelFileName = "test.xls";
    String headerKey = "Content-Disposition";
    String headerValue = String.format("attachment; filename=\"%s\"",
            excelFileName);
    response.setHeader(headerKey, headerValue);

    //Here I create the workbook that I want to download
    ProjectMonthReport report = reportService.getReport(projectNumber, month, year);
    //ExcelService builts the workbook using POI
    Workbook workbook = excelService.exportExcel(report, employee, tsKey);


    //The response is stored in an outputstream
    OutputStream out = response.getOutputStream();
    response.setContentType("application/vnd.ms-excel");
    byte[] byteArray = ((HSSFWorkbook)workbook).getBytes();
    out.write(byteArray);
    out.flush();
    out.close();


    //Wrote this one just for testing if the file is already corrupt here. --> It's fine.
    FileOutputStream fos = new FileOutputStream("C:\dev\directDownload.xls");
    fos.write(byteArray);
    fos.flush();
    fos.close();
}

使用POI建档的Java服务方式:

     public Workbook exportExcel(final ProjectMonthReport report, final int employee, final String tsKey) throws IOException,
        InvalidFormatException {
    Workbook workbook = new HSSFWorkbook();

    CreationHelper createHelper = workbook.getCreationHelper();

    // Create a Sheet
    Sheet sheet = workbook.createSheet("Employee");

    // Create a Font for styling header cells
    Font headerFont = workbook.createFont();
    headerFont.setBold(true);
    headerFont.setFontHeightInPoints((short) 14);
    headerFont.setColor(IndexedColors.RED.getIndex());

    // Create a CellStyle with the font
    CellStyle headerCellStyle = workbook.createCellStyle();
    headerCellStyle.setFont(headerFont);

    Row headeRow = sheet.createRow(0);
    Cell dateHeader = headeRow.createCell(0);
    dateHeader.setCellValue("Datum");
    Cell startHeader = headeRow.createCell(1);
    startHeader.setCellValue("Beginn");
    Cell endHeader = headeRow.createCell(2);
    endHeader.setCellValue("Ende");
    Cell activityHeader = headeRow.createCell(3);
    activityHeader.setCellValue("Tätigkeitsbereit");
    Cell cardHeader = headeRow.createCell(4);
    cardHeader.setCellValue("Kartennummer");

    List<WorkDescriptionDetail> details = report.getEmployees().get(employee).getKeyDetailMap().get(Integer.valueOf(tsKey)).getDetailList();

    int counter = 1;
    for (WorkDescriptionDetail detail : details) {

        List <String> stringList= detail.toStringList();

        Row row = sheet.createRow(counter);
        Cell cellDate = row.createCell(0);
        cellDate.setCellValue(stringList.get(0));

        Cell cellStart = row.createCell(1);
        cellStart.setCellValue(stringList.get(1));

        Cell cellEnd = row.createCell(2);
        cellEnd.setCellValue(stringList.get(2));

        Cell cellActivity = row.createCell(3);
        cellActivity.setCellValue(stringList.get(3));
        counter ++;
    }

    return workbook;
}

我的angular组件:

  saveFile(employee: string, tsKey:string) {
   this.subscription = this.reportService.saveXlsFile(this.projectNumber, this.year, this.month, employee, tsKey)
        .subscribe(response=> {
            console.log(response);
                let mediatype = 'application/vnd.ms-excel;charset=UTF-8';

                const data  = new Blob(["\ufeff",response.arrayBuffer()], {type: mediatype});
                console.log(data);
                saveAs(data, 'test.xls');

            },
            error => console.log("error downloading the file"));

}

调用的Ts服务函数:

    saveXlsFile(projectNumber:string, year:string, month:string, empId: string, tsKey:string) {
    let params:URLSearchParams = new URLSearchParams();
    params.set('projectNumber', projectNumber);
    console.log(projectNumber);
    params.set('month', month);
    console.log(month);
    params.set( 'year', year);
    console.log(year);
    params.set('employee', empId);
    console.log(empId);
    params.set('tsKey', tsKey);
    console.log(tsKey);

    return this.http.get(this.baseUrl + "/file", { search: params } );
}

我尝试通过 Postman 检索响应并直接下载文件。当我这样做时,excel 无法打开文件(Excel 刚刚崩溃),但是我可以在 OpenOffice 版本中打开文件并且工作正常。它也没有损坏。

这几天我一直在网上搜索,我认为这可能是前端造成的一个环境问题。但也许 SpringBoot 也在玩我。有什么建议吗?

感谢您的帮助!

嘿,我昨天自己找到了解决这个问题的办法。在 angular 服务中添加以下内容:

   return this.http.get(this.baseUrl + "/file", { search: params, responseType: ResponseContentType.Blob }).map(
        (res) => {
            return new Blob([res.blob()], { type: 'application/vnd.ms-excel' });
        });

之后您需要像这样修改组件:

   saveFile(employee: string, tsKey:string) {
   this.subscription = this.reportService.saveXlsFile(this.projectNumber, this.year, this.month, employee, tsKey)
        .subscribe(response=> {
            console.log(response);
                let mediatype = 'application/vnd.ms-excel';
                saveAs(response, 'test.xlsx');
            },
            error => console.log("error downloading the file"));
}

所以问题是我在响应中没有得到 blob 对象....