Angular2从Web API下载excel文件,文件已损坏

时间:2017-01-20 19:30:52

标签: angular

我正在尝试下载使用ClosedXML创建的文件。我已经验证该文件没有损坏,但由于某种原因,它只适用于Angular1,而不是Angular2。返回文件的web api代码是:

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;

在Angular2中,在我的网络服务中:

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

在我的组件中:

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

下载了一个文件,但是当我尝试访问它时它已损坏,并且在使用Angular1下载时文件的大小是两倍。

如果我使用Angular1调用SAME API,则可以正常下载该文件。

我的服务代码:

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

其中handleSuccess返回response.data(我无法获得angular2)

以及调用服务的代码:

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

有趣的是,在Angular2中,如果我只是简单地将我的web服务更改为GET(我想要POST,但只是尝试一下)然后摆脱服务代码并简单地进行此调用,文件很好:< / p>

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

所以,真的,这有什么区别?为什么相同或非常相似的代码适用于Angular1但不适用于Angular2 ??

- 凯伦

4 个答案:

答案 0 :(得分:3)

我知道其他人也发现了同样的问题。我已经解决了,但必须切换到xhr才能使其正常工作。

第一种方法是不起作用的方法。我从上面稍微简化了一下:

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

第二种方法是工作的方法:

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

    });
}

希望这会对别人有所帮助!我在其他地方看到了这个问题,所以我也会在那里添加一个链接到我的解决方案。

- 凯伦

答案 1 :(得分:1)

这对我有用(确保服务确实将xlsx文件作为响应发回):

  1. 为show“save file”弹出窗口

    安装这些依赖项
    npm install file-saver --save-dev
    npm install @types/file-saver --save-dev
    
  2. 导入您的服务:

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

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

答案 2 :(得分:1)

这是我用Dwobnload加载样式(颜色..)

的完整文件的解决方案

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


        }

这是角度服务:

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

最后使用FileSaver Api

的Angular组件方法
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.');  
      }

我希望能帮助你。

答案 3 :(得分:0)

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