Search code examples
node.jsangularexceljs

Downloading excel file using ExcelJS and node js not being downloaded in any browser


I was trying to send .xlsx file using exceljs and nodejs to the client as a downloadable file. So far I'm able to create employees.xlsx file but it's not triggering save file dialog in the browser. Here's what I've tried in my backend API:

My Controller:

exports.exportEmployees = async (req, res) => {

  try {
    const employees = await Employee.find();
    let workbook = new Excel.Workbook();
    let worksheet = workbook.addWorksheet("Employees");
    worksheet.columns = [
      { header: "Employee Name", key: "fullName", width: 40 },
      { header: "Department", key: "departmentName", width: 25 },
      { header: "Position", key: "positionName", width: 25 },
    ];

    worksheet.addRows(employees);

    res.setHeader(
      "Content-Type",
      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
    );
    res.setHeader("Content-Disposition", "attachment; filename=employees.xlsx");

    // This doesn't work either
    // workbook.xlsx.write(res).then(function () {
    //   res.status(200).end();
    // });

    workbook.xlsx.writeFile("./employees.xlsx").then(
      (response) => {
        console.log("File is created"); // I'm able to see this in my console
        console.log(path.join(__dirname, "../employees.xlsx"));
        res.sendFile(path.join(__dirname, "../employees.xlsx"));
      },
      (err) => {
        console.log("ERROR: ", err);
      }
    );
  } catch (err) {
    res.status(500).json({ errors: err });
  }
};

And in my route.js

router.get("/employee-excel", auth, exportExcelController.exportEmployees);

module.exports = router;

So, the problem I'm facing now is whenever I call the api from my Angular app the browser response is a binary code.

Angular service to call the API

generateEmployeeExcel(query: any): Observable<any> {
    return this.http.get(`${this.serverReportAPI}/employee-excel`, {
      params: query,
      headers: this.tokenHelperService.getAuthToken().headers,
    });
  }

My component

this.reportService.generateEmployeeExcel(query).subscribe(
  (response) => {
    console.log("Are we getting here?", response); // not able to get here
    this.isLoading = false;
  },
  (err) => {
    console.log("Is there any error?", err); // This is displayed in console

    this.isLoading = false;
  }
);

Any help would be appreciated.

The response in my browser enter image description here


Solution

  • Change http code to :-

    generateEmployeeExcel(query: any): Observable<any> {
        return this.http.get(`${this.serverReportAPI}/employee-excel`, {
          params: query,
          headers: this.tokenHelperService.getAuthToken().headers,
          responseType: 'blob'
        });
      }
    

    For subscribe and download :-

    this.reportService.generateEmployeeExcel(query).subscribe(
      (res) => {
            console.log("Are we getting here?", res); // not able to get here
            this.isLoading = false;
            let url = window.URL.createObjectURL(res.data);
            let a = document.createElement('a');
            document.body.appendChild(a);
            a.setAttribute('style', 'display: none');
            a.href = url;
            a.download = "employee.xlsx";
            a.click();
            window.URL.revokeObjectURL(url);
            a.remove();
      },
      (err) => {
        console.log("Is there any error?", err); // This is displayed in console
    
        this.isLoading = false;
      }
    );
    

    If support for IE is required :-

    this.reportService.generateEmployeeExcel(query).subscribe(
      (res) => {
            console.log("Are we getting here?", res); // not able to get here
         if(window.navigator.msSaveBlob) {
             window.nagigator.msSaveBlob(res, "employee.xlsx");
             return;
         }
            this.isLoading = false;
            let url = window.URL.createObjectURL(res.data);
            let a = document.createElement('a');
            document.body.appendChild(a);
            a.setAttribute('style', 'display: none');
            a.href = url;
            a.download = "employee.xlsx";
            a.click();
            window.URL.revokeObjectURL(url);
            a.remove();
      },
      (err) => {
        console.log("Is there any error?", err); // This is displayed in console
    
        this.isLoading = false;
      }
    );