Angular2 download excel file from Web API, file is

2020-02-13 03:23发布

问题:

I am trying to download a file that I created with ClosedXML. I have verified that the file is not corrupt but, for some reason, it works just with Angular1, not Angular2. The web api code to return the file is:

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

A file IS downloaded, but it is corrupt when I try to access it and is TWICE the size of the file when downloaded with Angular1.

If I call the SAME API with Angular1, the file is downloaded fine.

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 can't get at for angular2)

and the code to invoke 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 enough, in Angular2, if I simply change my webservice to a GET (I wanted POST however, but just to try it out) then got rid of the service code and simply made this call, the file is fine:

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

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

-- Karen

回答1:

I know that others have found the same problem. I have resolved it, but had to switch to xhr to make it work.

This first method is the one that does NOT work. I simplified it somewhat from above:

    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 DOES work:

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

    });
}

Hopefully this will help others! I saw this issue posted elsewhere so I will add a link to my solution there as well.

-- Karen



回答2:

This is what worked for me(make sure that service indeed sends back xlsx file as response):

  1. Install these dependencies for show "save file" pop-up

    npm install file-saver --save-dev
    npm install @types/file-saver --save-dev
    
  2. Import in your service:

    import * as FileSaver from 'file-saver';
    
  3. Usage:

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


回答3:

this is the solution I used to Dwobnload the complete file with style (colors..)

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

And finally the Angular component method 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.');  
      }

I hope that helps you.



回答4:

Latest solution Try If using Angular 5:

///angular side

//header
let expheaders = new Headers({ 'Content-Type': 'application/json', 'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
let options = new RequestOptions({ headers: expheaders, responseType: ResponseContentType.Blob });


this.http.post(this.modeleffectiveperiodserviceurl+"ExportToXlsx",JSON.stringify({
                           modeleffectiveperioddetails:model,
                           colheader:columns
                           }),options)
                           .subscribe(data => { 
                             var blob = new Blob([data.blob()], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
                             var url= window.URL.createObjectURL(blob);
                             window.open(url);                   
                          });



//////////server side


  byte[] bytes = this._exportmanager.GetExcelFromServer(modeleffectivepc, colheader);
            HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
            httpResponseMessage.Content = new ByteArrayContent(bytes.ToArray());
            httpResponseMessage.Content.Headers.Add("x-filename", fileName);
            httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
            httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
            httpResponseMessage.Content.Headers.ContentDisposition.FileName = fileName;
            httpResponseMessage.StatusCode = HttpStatusCode.OK;
            return httpResponseMessage;

please tell if any problem


标签: angular