2

I have a simple service on Angular 2 and Typescript that requests Excel files to a server and then opens a download file dialogue for the user. However, as it is currently, the file becomes corrupt when downloaded.

When downloaded, it opens fine in OpenOffice and derivates, but throws a "File is Corrupt" error on Microsoft Excel, and asks if the user wants to recover as much as it can.

When Excel is prompted to recover the file, it does so successfully, and the recovered Excel has all rows and data that is expected for the Excel file. Comparing the recovered file against opening the file in OpenOffice and derivates evidence no outstanding differences.

The concrete Excel I am trying to download is generated with Apache POI in a microservice, then passed to the main backend and finally served to the frontend for the user to download. Both the backend and microservice are written in Java, through Spark Framework.

I made some tests on the backends, and concluded the problem is not the report generation nor the data transfer:

  1. Asking the microservice to save the generated Excel in a file within the server and then opening such file (hereby file A) in Excel shows that file A is not corrupted.

  2. Asking the main backend server to save the Excel file that it receives from the microservice in a file within itself and then opening such file in Excel (hereby file B) shows that file B is not corrupted.

  3. Downloading both file A and file B through FileZilla from their respective servers yields completely uncorrupted files.

As such, I believe it is safe to assume the Excel becomes corrupted somewhere between the time the file is received on the frontend and the time the user downloads such file. Additionally, the Catalina logs do not evidence any error that might potentially be happening.

I have read several posts that deal with the issue, including a bug report (https://github.com/angular/angular/issues/14083) that included a workaround via XMLHTTPRequest. However, none of the workarounds detailed were successful in solving my issue.

Attached is the code I am using to both obtain the Excel file from the backend and serve it to the user. I am including both an XMLHTTPRequest and an Angular http call (within comments) since those are the two main ways I have been trying to make this work. Additionally, please do take into account the code has been altered to remove information I do not wish to make public.

download(body) {
    let reply = Observable.create(observer => {
      let xhr = new XMLHttpRequest();

      xhr.open('POST', 'URL', true);
      xhr.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
      xhr.setRequestHeader('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
      xhr.setRequestHeader('Authorization', 'REDACTED');

      xhr.responseType = 'blob';

      xhr.onreadystatechange = function () {
        if(xhr.readyState === 4) {
          if(xhr.status === 200) {
            var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
            var blob = new Blob([xhr.response], { type: contentType });
            observer.next(blob);
            observer.complete();
          }

          else {
            observer.error(xhr.response);
          }
        }
      }

      xhr.send(JSON.stringify(body));
    });

    return reply;


    /*let headers = new Headers();
    headers.set("Authorization", 'REDACTED');
    headers.set("Accept", 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');

    let requestOptions :RequestOptions = new RequestOptions({headers: headers, responseType: ResponseContentType.Blob});

    return this.http.post('URL', body, requestOptions);*/
  }

Hereby is the code to prompt the user to download the Excel. It is currently made to work with the XMLHTTPRequest. Please do note that I have also attempted to download without resorting to FileSaver, with no luck.

downloadExcel(data) {
    let body = {
      /*REDACTED*/
    }

    this.service.download(body)
        .subscribe(data => {
          FileSaver.saveAs(data, "Excel.xlsx");
        });
  }

Hereby are the versions of the tools I am using:

  • NPM: 5.6.0
  • NodeJs: 8.11.3
  • Angular JS: ^6.1.0
  • Browsers used: Chrome, Firefox, Edge.

Any help on this issue would be appreciated. Any additional information you may need I will be happy to provide.

Magnaillusion
  • 65
  • 2
  • 10

2 Answers2

0

I think what you want is CSV format which open in Excel, update your sevice as follow:

You should tell Angular you are expecting a response of type blob (Binary Large Object) that is your Excel/Csv file.

Also make sure the URL/API on your server is set to accept content-type='text/csv'.

Here's an example with Angular 2.

@Injectable()
export class YourService {

    constructor(private http: Http) {}

    download() { //get file from the server
        this.http.get("http://localhost/..", {
            responseType: ResponseContentType.Blob,
            headers: new Headers({'Content-Type', 'text/csv'})
        }).subscribe(
            response => {
                var blob = new Blob([response.blob()], {type: 'text/csv'});
                FileSaver.saveAs(blob, 'yourFileName.csv');
            },
            error => {
                console.error('something went wrong');
            }
        );
    }
}
mkhayata
  • 327
  • 1
  • 3
  • 11
  • I am sure I am expecting an actual XLSX file. I made sure to ask POI to explicitly construct the `XSSFWorkbook` with `XSSFWorkbookType.XLSX`. Response's type is also set to XLSX format. I'm not sure my problem has to do with the file format, since it would then make no sense for the file to be openable at all. Something happens in the frontend that makes Excel not like what it sees, but only MS Excel (OOffice reads the file just fine), and only if the file is downloaded through the angular service (Excel doesn't complain when the file is saved directly on the server, and then opened). – Magnaillusion Oct 29 '18 at 19:52
  • I would say even though it is XLSX, try the solution above with ```Content-Type 'text/csv'``` – mkhayata Oct 31 '18 at 02:36
-1

Have you tried uploading/downloading your xls file as base64?

var encodedXLSToUpload = 'data:application/xls;base64,' + btoa(file);

Check this for more details: Creating a Blob from a base64 string in JavaScript

ronnie bermejo
  • 498
  • 4
  • 10