Angular 2 download PDF from API and Display it in

2020-01-24 07:45发布

I'm learning Angular 2 Beta. I wonder how to download the PDF file from the API and display it in my view? I've tried to make a request using the following:

    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);
    })
  • Please note that I only use the console.log to see the value of r

But I always get the following exception message:

"arrayBuffer()" method not implemented on Response superclass

Is it because that method isn't ready yet in Angular 2 Beta? Or is there any mistake that I made?

Any help would be appreciated. Thank you very much.

标签: pdf angular
9条回答
家丑人穷心不美
2楼-- · 2020-01-24 08:07

Here is the simplest way to download a file from an API that I was able to come up with.

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

}

Call the downloadFile(api,fileName) method from your component class.

To get FileSaver run the following commands in your terminal

npm install file-saver --save
npm install @types/file-saver --save
查看更多
我只想做你的唯一
3楼-- · 2020-01-24 08:07

Here is the code that works for downloadign the API respone in IE and chrome/safari. Here response variable is API response.

Note: http call from client needs to support blob response.

    let blob = new Blob([response], {type: 'application/pdf'});
    let fileUrl = window.URL.createObjectURL(blob);
    if (window.navigator.msSaveOrOpenBlob) {
        window.navigator.msSaveOrOpenBlob(blob, fileUrl.split(':')[1] + '.pdf');
    } else {
        window.open(fileUrl);
    }
查看更多
放我归山
4楼-- · 2020-01-24 08:10

Working solution with C# Web API loading PDF as a byte array:

C# loads PDF as a byte array and converts to Base64 encoded string

public HttpResponseMessage GetPdf(Guid id)
{
    byte[] file = GetFile(id);
    HttpResponseMessage result = Request.CreateResponse(HttpStatusCode.OK);
    result.Content = new StringContent("data:application/pdf;base64," + Convert.ToBase64String(file));
    return result;
}

Angular service gets PDF

getPdf(): Observable<string> {
    return this.http.get(webApiRequest).pipe(
        map(response => {
            var anonymous = <any>response;
            return anonymous._body;
        })
    );
}

Component view embeds the PDF via binding to service response

The pdfSource variable below is the returned value from the service.

<embed [src]="sanitizer.bypassSecurityTrustResourceUrl(pdfSource)" type="application/pdf" width="100%" height="300px" />

See the Angular DomSanitizer docs for more info.

查看更多
We Are One
5楼-- · 2020-01-24 08:11

I don't think all of these hacks are necessary. I just did a quick test with the standard http service in angular 2.0, and it worked as expected.

/* 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 */ }
        );
}
查看更多
Explosion°爆炸
6楼-- · 2020-01-24 08:15

So here is how I managed to get it to work. My situation: I needed to download a PDF from my API endpoint, and save the result as a PDF in the browser.

To support file-saving in all browsers, I used the FileSaver.js module.

I created a component that takes the ID of the file to download as parameter. The component, , is called like this:

<pdf-downloader no="24234232"></pdf-downloader>

The component itself uses XHR to fetch/save the file with the number given in the no parameter. This way we can circumvent the fact that the Angular2 http module doesn't yet support binary result types.

And now, without further ado, the component code:

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

I've used Font Awesome for the fonts used in the template. I wanted the component to display a download button and a spinner while the pdf is fetched.

Also, notice I could use require to fetch the fileSaver.js module. This is because I'm using WebPack so I can require/import like I want. Your syntax might be different depending of your build tool.

查看更多
Explosion°爆炸
7楼-- · 2020-01-24 08:15

Hello, here is a working example. It is also suitable for PDF! application/octet-stream - general type. Controller:

public FileResult exportExcelTest()
{ 
    var contentType = "application/octet-stream";
    HttpContext.Response.ContentType = contentType;

    RealisationsReportExcell reportExcell = new RealisationsReportExcell();     
    byte[] filedata = reportExcell.RunSample1();

    FileContentResult result = new FileContentResult(filedata, contentType)
    {
        FileDownloadName = "report.xlsx"
    };
    return result;
}

Angular2:

Service xhr:

import { Injectable } from '@angular/core';
import { BrowserXhr } from '@angular/http';

@Injectable()
export class CustomBrowserXhr extends BrowserXhr {
  constructor() {
      super();
  }

  public build(): any {
      let xhr = super.build();
      xhr.responseType = "blob";
      return <any>(xhr);
  }   
}

Install file-saver npm packages "file-saver": "^1.3.3", "@types/file-saver": "0.0.0" and include in vendor.ts import 'file-saver';

Component btn download.

import { Component, OnInit, Input } from "@angular/core";
import { Http, ResponseContentType } from '@angular/http';
import { CustomBrowserXhr } from '../services/customBrowserXhr.service';
import * as FileSaver from 'file-saver';

@Component({
    selector: 'download-btn',
    template: '<button type="button" (click)="downloadFile()">Download</button>',
    providers: [
        { provide: CustomBrowserXhr, useClass: CustomBrowserXhr }
    ]
})

export class DownloadComponent {        
    @Input() api: string; 

    constructor(private http: Http) {
    }

    public downloadFile() {
        return this.http.get(this.api, { responseType: ResponseContentType.Blob })
        .subscribe(
            (res: any) =>
            {
                let blob = res.blob();
                let filename = 'report.xlsx';
                FileSaver.saveAs(blob, filename);
            }
        );
    }
}

Using

<download-btn api="api/realisations/realisationsExcel"></download-btn>
查看更多
登录 后发表回答