Angular2 download excel file from Web API, file is damaged

I am trying to upload a file created using ClosedXML. I checked that the file is not corrupted, but for some reason it only works with Angular1, not Angular2. Web api code to return the file:

HttpResponseMessage response = new HttpResponseMessage(); response.StatusCode = HttpStatusCode.OK; response.Content = new ByteArrayContent(ms.GetBuffer()); response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); return response; 

In Angular2, in my web service:

 let headers = new Headers(); headers.append('Content-Type', 'application/json'); headers.append('responseType', 'arrayBuffer'); this.observableDataGet = this._http.post(`${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet`, {headers: this.getHeaders()}) .map(response => { if (response.status == 400) { return "FAILURE"; } else if (response.status == 200) { var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; var blob = new Blob([response.arrayBuffer()], { type: contentType }); return blob; } }) 

and in my component:

 .subscribe(blob => { var downloadUrl= URL.createObjectURL(blob); window.open(downloadUrl); }, 

The file loads, but it gets corrupted when I try to access it, and TWICE the file size when downloading using Angular1.

If I call the SAME API using Angular1, the file will download perfectly.

My service code:

 function generateMonthlySpreadsheet(header) { var request = $http({ method: "post", responseType: 'arraybuffer', url: TEST_API_URL + 'Report/MonthlySpreadsheet', timeout: 30000, headers: header }); return ( request.then(handleSuccess, handleError) ); } 

where handleSuccess returns response.data (which I cannot get for angular2)

and code to call the service:

 alertAppService.generateMonthlySpreadsheet(header).then(function (data){ var blob = new Blob([data], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}); var objectUrl = URL.createObjectURL(blob); window.open(objectUrl); 

Interestingly, in Angular2, if I just changed my web service to GET (then I wanted to POST, but just try), then he got rid of the service code and just made this call, the file is fine

 window.open(`${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet`, "_blank"); 

So what is the difference? Why does the same or very similar code work for Angular1 but not Angular2 ??

- Karen

+2
angular
source share
3 answers

I know others have found the same problem. I solved this, but I had to switch to xhr for it to work.

This first method is one that DOES NOT work. I simplified it somewhat:

  generateMonthlySpreadsheet2(searchCriteria: Object) { let headers = new Headers(); headers.append('Content-Type', 'application/json'); headers.append('responseType', 'blob'); return this._http.post(`${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet`, {headers: headers}) .map(response => { if (response.status == 400) { this.handleError; } else if (response.status == 200) { var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; var blob = new Blob([(<any>response)._body], { type: contentType }); // size is 89KB instead of 52KB // var blob = new Blob([(<any>response).arrayBuffer()], { type: contentType }); // size is 98KB instead of 52KB // var blob = new Blob([(<any>response).blob()], { type: contentType }); // received Error: The request body isn't either a blob or an array buffer return blob; } }) .catch(this.handleError); } 

This second method is the one that works:

 generateMonthlySpreadsheet(searchCriteria: Object): Observable<Object[]> { return Observable.create(observer => { let xhr = new XMLHttpRequest(); xhr.open('POST', `${AppSettings.REPORTS_API_URL}/Report/MonthlySpreadsheet`, true); xhr.setRequestHeader('Content-type', 'application/json'); 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(); }); } 

Hope this helps others! I saw that this question is posted elsewhere, so I will also add a link to my solution.

- Karen

+3
source share

This is what worked for me (make sure the service does send the xlsx file back as an answer):

  • Set these dependencies to display the save file popup.

     npm install file-saver --save-dev npm install @types/file-saver --save-dev 
  • Import to your service:

     import * as FileSaver from 'file-saver'; 
  • Using:

     downloadFile(): void { let url: string = "http://yourdomain.com/exports/excelExport.aspx"; let headers = new Headers({ 'Content-Type': 'application/json'} ); //in case you have custom headers, else you can ignore this part headers.set('x-custom-header1', "some value"); headers.set('x-custom-header2', "some value2"); let options = new RequestOptions({responseType: ResponseContentType.Blob, headers }); this.http.get(url, options) .map(res => res.blob()) .subscribe( data => { FileSaver.saveAs(data, 'Export.xlsx'); }, err => { console.log('error'); console.error(err); }); } 
+1
source share

this is the solution i used for dwobnload the full file with style (color ..)

WebApi:

  [HttpGet] [Route("api/RapproResult/DownloadExcelReport/{reportName}")] public HttpResponseMessage DownloadExcelReport( string reportName) { try { string filePath = HttpContext.Current.Server.MapPath("~/Report/Report_TEST.XLS"); if (!string.IsNullOrEmpty(filePath )) { using (MemoryStream ms = new MemoryStream()) { using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read)) { byte[] bytes = new byte[file.Length]; file.Read(bytes, 0, (int)file.Length); ms.Write(bytes, 0, (int)file.Length); HttpResponseMessage httpResponseMessage = new HttpResponseMessage(); httpResponseMessage.Content = new ByteArrayContent(ms.GetBuffer()); httpResponseMessage.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); httpResponseMessage.StatusCode = HttpStatusCode.OK; return httpResponseMessage; } } } return this.Request.CreateResponse(HttpStatusCode.NotFound, "File not found."); } catch (Exception ex) { return this.Request.CreateResponse(HttpStatusCode.InternalServerError, ex); } } 

this is angular service:

  protected downloadExcelReportService( reportName: string) { let completeUrl = this.downloadExcelReportUrl+reportName; return Observable.create(observer => { let xhr = new XMLHttpRequest(); xhr.open('GET', completeUrl, true); xhr.setRequestHeader('Content-type', 'application/json'); xhr.responseType='blob'; xhr.onreadystatechange = function () { if (xhr.readyState === 4) { if (xhr.status === 200) { debugger; var contentType = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; var blob = new Blob([xhr.response], { type: contentType }); observer.next(blob); observer.complete(); return observer; } else { observer.error(xhr.response); } } } debugger; xhr.send(); }); } 

Finally, the angular component using FileSaver Api

 import * as FileSaver from "file-saver"; downloadexcelReport(data) { this._servive.downloadExcelReport(data.RapproName) .subscribe( _data => FileSaver.saveAs(_data, data.RapproName+".xls" ) ), error => console.log("Error downloading the file."), () => console.log('Completed file download.'); } 

Hope this helps you.

+1
source share

All Articles