Angular 2从API下载PDF并在View中显示它

asu*_*sky 26 pdf angular

我正在学习Angular 2 Beta.我想知道如何从API下载PDF文件并在我的视图中显示它?我试图使用以下内容提出请求:

    var headers = new Headers();
    headers.append('Accept', 'application/pdf');
    var options = new ResponseOptions({
        headers: headers
    });
    var response = new Response(options);
    this.http.get(this.setUrl(endpoint), response).map(res => res.arrayBuffer()).subscribe(r=>{
       console.log(r);
    })
Run Code Online (Sandbox Code Playgroud)
  • 请注意,我只使用它console.log来查看值r

但我总是收到以下异常消息:

"arrayBuffer()"方法未在Response超类上实现

是因为Angular 2 Beta中尚未准备好该方法吗?或者我犯了什么错误?

任何帮助,将不胜感激.非常感谢你.

Thi*_*ier 17

实际上,HTTP支持尚未实现此功能.

作为一种解决方法,您需要扩展BrowserXhrAngular2 的类,如下所述,responseTypeblob在底层xhr对象上设置to :

import {Injectable} from 'angular2/core';
import {BrowserXhr} from 'angular2/http';

@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
  constructor() {}
  build(): any {
    let xhr = super.build();
    xhr.responseType = "blob";
    return <any>(xhr);
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要将响应有效负载包装到Blob对象中,并使用FileSaver库打开下载对话框:

downloadFile() {
  this.http.get(
    'https://mapapi.apispark.net/v1/images/Granizo.pdf').subscribe(
      (response) => {
        var mediaType = 'application/pdf';
        var blob = new Blob([response._body], {type: mediaType});
        var filename = 'test.pdf';
        saveAs(blob, filename);
      });
}
Run Code Online (Sandbox Code Playgroud)

FileSaver库必须包含到HTML文件中:

<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

看到这个plunkr:http://plnkr.co/edit/tfpS9k2YOO1bMgXBky5Y?p =preview

不幸的是,这将为responseType所有AJAX请求设置.为了能够设置此属性的值,还需要在XHRConnectionHttp类中进行更多更新.

作为参考,请看这些链接:

编辑

在考虑了一点之后,我认为您可以利用分层注入器并仅在执行下载的组件级别配置此提供程序:

@Component({
  selector: 'download',
  template: '<div (click)="downloadFile() ">Download</div>'
  , providers: [
    provide(CustomBrowserXhr, 
      { useClass: CustomBrowserXhr }
  ]
})
export class DownloadComponent {
  @Input()
  filename:string;

  constructor(private http:Http) {
  }

  downloadFile() {
    this.http.get(
      'https://mapapi.apispark.net/v1/images/'+this.filename).subscribe(
        (response) => {
          var mediaType = 'application/pdf';
          var blob = new Blob([response._body], {type: mediaType});
          var filename = 'test.pdf';
          saveAs(blob, filename);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

此覆盖仅适用于此组件(在引导应用程序时不要忘记删除相应的提供).下载组件可以像这样使用:

@Component({
  selector: 'somecomponent',
  template: `
    <download filename="'Granizo.pdf'"></download>
  `
  , directives: [ DownloadComponent ]
})
Run Code Online (Sandbox Code Playgroud)

  • 嗨再次 - 抱歉发送垃圾邮件 - 只是更新.我无法通过"_body is private property"问题,所以我决定使用原始的XMLHttpRequest实例化.不是最优雅的解决方案,但它的工作原理.......在程序员悲惨的生活中再度过一天:).谢谢@ThierryTemplier的帮助 (2认同)

Spo*_*ck 17

所以这就是我如何设法让它发挥作用.我的情况:我需要从API端点下载PDF,并将结果作为PDF保存在浏览器中.

为了支持所有浏览器中的文件保存,我使用了FileSaver.js 模块.

我创建了一个组件,它将文件的ID作为参数下载.组件,,被称为这样:

<pdf-downloader no="24234232"></pdf-downloader>
Run Code Online (Sandbox Code Playgroud)

组件本身使用XHR以no参数中给出的数字来获取/保存文件.这样我们就可以避免Angular2 http模块还不支持二进制结果类型这一事实.

现在,不用多说,组件代码:

    import {Component,Input } from 'angular2/core';
    import {BrowserXhr} from 'angular2/http';

    // Use Filesaver.js to save binary to file
    // https://github.com/eligrey/FileSaver.js/
    let fileSaver = require('filesaver.js');


    @Component({
      selector: 'pdf-downloader',
      template: `
        <button
           class="btn btn-secondary-outline btn-sm "
          (click)="download()">
            <span class="fa fa-download" *ngIf="!pending"></span>
            <span class="fa fa-refresh fa-spin" *ngIf="pending"></span>
        </button>
        `
   })

   export class PdfDownloader  {

       @Input() no: any;

       public pending:boolean = false;

       constructor() {}

       public download() {

        // Xhr creates new context so we need to create reference to this
        let self = this;

        // Status flag used in the template.
        this.pending = true;

        // Create the Xhr request object
        let xhr = new XMLHttpRequest();
        let url =  `/api/pdf/iticket/${this.no}?lang=en`;
        xhr.open('GET', url, true);
        xhr.responseType = 'blob';

        // Xhr callback when we get a result back
        // We are not using arrow function because we need the 'this' context
        xhr.onreadystatechange = function() {

            // We use setTimeout to trigger change detection in Zones
            setTimeout( () => { self.pending = false; }, 0);

            // If we get an HTTP status OK (200), save the file using fileSaver
            if(xhr.readyState === 4 && xhr.status === 200) {
                var blob = new Blob([this.response], {type: 'application/pdf'});
                fileSaver.saveAs(blob, 'Report.pdf');
            }
        };

        // Start the Ajax request
        xhr.send();
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经将Font Awesome用于模板中使用的字体.我希望组件在获取pdf时显示下载按钮和微调器.

另外,请注意我可以使用require来获取fileSaver.js模块.这是因为我正在使用WebPack所以我可以像我想要的那样需要/导入.根据您的构建工具,您的语法可能会有所不同.

  • 非常感谢!这非常有效. (2认同)

小智 7

我不认为所有这些黑客都是必要的.我只是使用角度2.0中的标准http服务进行了快速测试,它按预期工作.

/* generic download mechanism */
public download(url: string, data: Object = null): Observable<Response> {

    //if custom headers are required, add them here
    let headers = new Headers();        

    //add search parameters, if any
    let params = new URLSearchParams();
    if (data) {
        for (let key in data) {
            params.set(key, data[key]);
        }
    }

    //create an instance of requestOptions 
    let requestOptions = new RequestOptions({
        headers: headers,
        search: params
    });

    //any other requestOptions
    requestOptions.method = RequestMethod.Get;
    requestOptions.url = url;
    requestOptions.responseType = ResponseContentType.Blob;

    //create a generic request object with the above requestOptions
    let request = new Request(requestOptions);

    //get the file
    return this.http.request(request)
        .catch(err => {
            /* handle errors */
        });      
}


/* downloads a csv report file generated on the server based on search criteria specified. Save using fileSaver.js. */
downloadSomethingSpecifc(searchCriteria: SearchCriteria): void {

    download(this.url, searchCriteria) 
        .subscribe(
            response => {                                
                let file = response.blob();
                console.log(file.size + " bytes file downloaded. File type: ", file.type);                
                saveAs(file, 'myCSV_Report.csv');
            },
            error => { /* handle errors */ }
        );
}
Run Code Online (Sandbox Code Playgroud)

  • 这里的'saveAs'功能是什么?如果它是https://github.com/eligrey/FileSaver.js/那么我认为最好包含一个导入来清楚它 (2认同)

She*_*har 6

这是从我能够提出的API下载文件的最简单方法.

import { Injectable } from '@angular/core';
import { Http, ResponseContentType } from "@angular/http";

import * as FileSaver from 'file-saver';

@Injectable()
export class FileDownloadService {


    constructor(private http: Http) { }

    downloadFile(api: string, fileName: string) {
        this.http.get(api, { responseType: 'blob' })
            .subscribe((file: Blob) => {
               FileSaver.saveAs(file, fileName);
        });    
    }

}
Run Code Online (Sandbox Code Playgroud)

downloadFile(api,fileName)从组件类中调用该方法.

要让FileSaver在终端中运行以下命令

npm install file-saver --save
npm install @types/file-saver --save
Run Code Online (Sandbox Code Playgroud)