我有一个WebApi / MVC应用程序,我正在为它开发一个angular2客户端(以取代MVC)。我在理解Angular如何保存文件时遇到了一些麻烦。

请求是可以的(与MVC一起工作很好,我们可以记录接收到的数据),但我不知道如何保存下载的数据(我主要遵循与本文相同的逻辑)。我确信这是愚蠢的简单,但到目前为止,我根本没有领会它。

组件函数的代码如下。我尝试了不同的替代方案,blob方式应该是我所理解的方式,但URL中没有createObjectURL函数。我甚至不能在窗口中找到URL的定义,但显然它存在。如果我使用FileSaver.js模块,我得到同样的错误。所以我猜这是最近改变的或者还没有实现的东西。我如何触发文件保存在A2?

downloadfile(type: string){

    let thefile = {};
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(data => thefile = new Blob([data], { type: "application/octet-stream" }), //console.log(data),
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    let url = window.URL.createObjectURL(thefile);
    window.open(url);
}

为了完整起见,获取数据的服务如下所示,但它所做的唯一一件事是发出请求并在没有映射的情况下传递数据:

downloadfile(runname: string, type: string){
   return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .catch(this.logAndPassOn);
}

当前回答

下载angular 2.4的*.zip解决方案。x:你必须从@angular/http导入ResponseContentType,并将responseType更改为ResponseContentType。ArrayBuffer(默认为ResponseContentType.Json)

getZip(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> {
 let headers = this.setHeaders({
      'Content-Type': 'application/zip',
      'Accept': 'application/zip'
    });

 return this.http.get(`${environment.apiUrl}${path}`, { 
   headers: headers, 
   search: params, 
   responseType: ResponseContentType.ArrayBuffer //magic
 })
          .catch(this.formatErrors)
          .map((res:Response) => res['_body']);
}

其他回答

 let headers = new Headers({
                'Content-Type': 'application/json',
                'MyApp-Application': 'AppName',
                'Accept': 'application/vnd.ms-excel'
            });
            let options = new RequestOptions({
                headers: headers,
                responseType: ResponseContentType.Blob
            });


this.http.post(this.urlName + '/services/exportNewUpc', localStorageValue, options)
                .subscribe(data => {
                    if (navigator.appVersion.toString().indexOf('.NET') > 0)
                    window.navigator.msSaveBlob(data.blob(), "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+".xlsx");

                    else {
                        var a = document.createElement("a");
                        a.href = URL.createObjectURL(data.blob());
                        a.download = "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+ ".xlsx";
                        a.click();
                    }
                    this.ui_loader = false;
                    this.selectedexport = 0;
                }, error => {
                    console.log(error.json());
                    this.ui_loader = false;
                    document.getElementById("exceptionerror").click();
                });

到目前为止,我发现这些答案既缺乏洞察力,也缺乏警告。你可以也应该注意与IE10+的不兼容性(如果你在乎的话)。

这是完整的示例,其中包括应用程序部分和服务部分。注意,我们设置了observe: "response"来捕捉文件名的头信息。还要注意,Content-Disposition报头必须由服务器设置并公开,否则当前的Angular HttpClient将不会传递它。我在下面添加了一段dotnet核心代码。

public exportAsExcelFile(dataId: InputData) {
    return this.http.get(this.apiUrl + `event/export/${event.id}`, {
        responseType: "blob",
        observe: "response"
    }).pipe(
        tap(response => {
            this.downloadFile(response.body, this.parseFilename(response.headers.get('Content-Disposition')));
        })
    );
}

private downloadFile(data: Blob, filename: string) {
    const blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8;'});
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    } else {
        const link = document.createElement('a');
        if (link.download !== undefined) {
            // Browsers that support HTML5 download attribute
            const url = URL.createObjectURL(blob);
            link.setAttribute('href', url);
            link.setAttribute('download', filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

private parseFilename(contentDisposition): string {
    if (!contentDisposition) return null;
    let matches = /filename="(.*?)"/g.exec(contentDisposition);

    return matches && matches.length > 1 ? matches[1] : null;
}

Dotnet核心,具有内容处理和媒体类型

 private object ConvertFileResponse(ExcelOutputDto excelOutput)
    {
        if (excelOutput != null)
        {
            ContentDisposition contentDisposition = new ContentDisposition
            {
                FileName = excelOutput.FileName.Contains(_excelExportService.XlsxExtension) ? excelOutput.FileName : "TeamsiteExport.xlsx",
                Inline = false
            };
            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
            Response.Headers.Add("Content-Disposition", contentDisposition.ToString());
            return File(excelOutput.ExcelSheet, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        }
        else
        {
            throw new UserFriendlyException("The excel output was empty due to no events.");
        }
    }

我分享了帮助我的解决方案(任何改进都非常感谢)

关于你的服务“pservice”:

getMyFileFromBackend(typeName: string): Observable<any>{
    let param = new URLSearchParams();
    param.set('type', typeName);
    // setting 'responseType: 2' tells angular that you are loading an arraybuffer
    return this.http.get(http://MYSITE/API/FILEIMPORT, {search: params, responseType: 2})
            .map(res => res.text())
            .catch((error:any) => Observable.throw(error || 'Server error'));
}

组成部分:

downloadfile(type: string){
   this.pservice.getMyFileFromBackend(typename).subscribe(
                    res => this.extractData(res),
                    (error:any) => Observable.throw(error || 'Server error')
                );
}

extractData(res: string){
    // transforme response to blob
    let myBlob: Blob = new Blob([res], {type: 'application/vnd.oasis.opendocument.spreadsheet'}); // replace the type by whatever type is your response

    var fileURL = URL.createObjectURL(myBlob);
    // Cross your fingers at this point and pray whatever you're used to pray
    window.open(fileURL);
}

在组件部分,无需订阅响应即可调用服务。订阅 有关openOffice mime类型的完整列表,请参见:http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html

下载和显示PDF文件,一个非常相似的代码剪辑如下:

  private downloadFile(data: Response): void {
    let blob = new Blob([data.blob()], { type: "application/pdf" });
    let url = window.URL.createObjectURL(blob);
    window.open(url);
  }

  public showFile(fileEndpointPath: string): void {
    let reqOpt: RequestOptions = this.getAcmOptions();  //  getAcmOptions is our helper method. Change this line according to request headers you need.
    reqOpt.responseType = ResponseContentType.Blob;
    this.http
      .get(fileEndpointPath, reqOpt)
      .subscribe(
        data => this.downloadFile(data),
        error => alert("Error downloading file!"),
        () => console.log("OK!")
      );
  }

问题是可观察对象运行在另一个上下文中,所以当你尝试创建URL var时,你有一个空对象,而不是你想要的blob。

解决这个问题的众多方法之一如下:

this._reportService.getReport().subscribe(data => this.downloadFile(data)),//console.log(data),
                 error => console.log('Error downloading the file.'),
                 () => console.info('OK');

当请求准备好时,它将调用函数"downloadFile",定义如下:

downloadFile(data: Response) {
  const blob = new Blob([data], { type: 'text/csv' });
  const url= window.URL.createObjectURL(blob);
  window.open(url);
}

这个blob已经被完美地创建了,所以URL变量,如果没有打开新的窗口,请检查你已经导入'rxjs/Rx';

import 'rxjs/Rx' ;

我希望这能帮助到你。