web-dev-qa-db-fra.com

Angular 2 télécharger PDF à partir de l'API et l'afficher dans la vue

J'apprends Angular 2 Beta. Je me demande comment télécharger le fichier PDF à partir de l'API et l'afficher à mon avis? J'ai essayé de le créer une demande utilisant les éléments suivants:

    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);
    })
  • Veuillez noter que je n'utilise que le console.log pour voir la valeur de r

Mais je reçois toujours le message d'exception suivant:

Méthode "arrayBuffer ()" non implémentée sur la superclasse Response

Est-ce parce que cette méthode n'est pas encore prête dans Angular 2 Beta? Ou est-ce une erreur que j'ai commise?

Toute aide serait appréciée. Merci beaucoup.

26
asubanovsky

En fait, cette fonctionnalité n'est pas encore implémentée dans le support HTTP.

En guise de solution de contournement, vous devez étendre la classe BrowserXhr d'angular2 comme décrit ci-dessous pour définir le responseType sur blob sur l'objet xhr sous-jacent:

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

Ensuite, vous devez encapsuler le contenu de la réponse dans un objet Blob et utiliser la bibliothèque FileSaver pour ouvrir la boîte de dialogue de téléchargement:

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

La bibliothèque FileSaver doit être incluse dans votre fichier HTML:

<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>

Voir ceci plunkr: http://plnkr.co/edit/tfpS9k2YOO1bMgXBky5Y?p=preview

Malheureusement, ceci va définir le responseType pour toutes les AJAX. Pour pouvoir définir la valeur de cette propriété, il y a plus de mises à jour à faire dans le XHRConnection et Http classes.

Comme références voir ces liens:

Modifier

Après avoir réfléchi un peu plus, je pense que vous pourriez exploiter les injecteurs hiérarchiques et configurer ce fournisseur uniquement au niveau du composant qui exécute le téléchargement:

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

Ce remplacement ne s’applique que pour ce composant (n'oubliez pas de supprimer la fourniture correspondante lors du démarrage de votre application). Le composant de téléchargement pourrait être utilisé comme ça:

@Component({
  selector: 'somecomponent',
  template: `
    <download filename="'Granizo.pdf'"></download>
  `
  , directives: [ DownloadComponent ]
})
17
Thierry Templier

Alors voici comment j'ai réussi à le faire fonctionner. Ma situation: je devais télécharger un PDF à partir de mon noeud final d'API) et enregistrer le résultat sous la forme d'un PDF dans le navigateur.

Pour prendre en charge l'enregistrement de fichiers dans tous les navigateurs, j'ai utilisé le module FileSaver.js .

J'ai créé un composant qui prend l'identifiant du fichier à télécharger en paramètre. Le composant, s'appelle comme ceci:

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

Le composant lui-même utilise XHR pour extraire/enregistrer le fichier avec le numéro indiqué dans le paramètre no . De cette façon, nous pouvons contourner le fait que le module http Angular2 ne supporte pas encore les types de résultats binaires.

Et maintenant, sans plus tarder, le code du composant:

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

J'ai utilisé Font Awesome pour les polices utilisées dans le modèle. Je voulais que le composant affiche un bouton de téléchargement et une visionneuse pendant que le fichier PDF est récupéré.

En outre, notez que je pourrais utiliser require pour récupérer le module fileSaver.js. En effet, j'utilise WebPack pour pouvoir exiger/importer comme je le souhaite. Votre syntaxe peut être différente en fonction de votre outil de génération.

17
Spock

Je ne pense pas que tous ces bidouilles soient nécessaires. Je viens de faire un test rapide avec le service http standard dans angular 2.0, et cela a fonctionné comme prévu.

/* 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 */ }
        );
}
7
Indev

Voici le moyen le plus simple de télécharger un fichier à partir d'une API que j'ai pu créer.

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

}

Appelez la méthode downloadFile(api,fileName) à partir de votre classe de composants.

Pour obtenir FileSaver, exécutez les commandes suivantes dans votre terminal

npm install file-saver --save
npm install @types/file-saver --save
6
Shekhar

Bonjour, voici un exemple de travail. Il convient également au format PDF! application/octet-stream - type général. Manette:

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

Installez les packages npm de sauvegardes de fichiers "économiseur de fichiers": "^ 1.3.3", "@ types/économiseur de fichiers": "0.0.0" et incluez dans vendor.ts import 'économiseur de fichiers';

Composant btn téléchargé.

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

En utilisant

<download-btn api="api/realisations/realisationsExcel"></download-btn>
4
dev-siberia

Pour que Filesaver travaille dans Angular 5: Installer

npm install file-saver --save
npm install @types/file-saver --save

Dans votre composant, utilisez import * as FileSaver from "file-saver";

et utilisez FileSaver .défaut et non FileSaver .SaveAs

.subscribe(data => {
            const blob = data.data;
            const filename = "filename.txt";
            FileSaver.default(blob, filename);
2
Thom Kiesewetter

Voici le code qui fonctionne pour downloadign l'API respone in IE et chrome/safari. Ici, la variable de réponse est la réponse de l'API.

Remarque: L'appel http du client doit prendre en charge la réponse blob.

    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);
    }
1
Dilip Nannaware
http
  .post(url, data, {
    responseType: "blob",
    observe: "response"
  })
  .pipe(
    map(response => {
      saveAs(response.body, "fileName.pdf");
    })
  );
0
Eylon Sultan

Solution de travail avec l'API Web C # chargement PDF sous forme de tableau d'octets:

C # charge PDF sous forme de tableau d'octets et est converti en chaîne codée en Base64

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

Le service angulaire reçoit le PDF

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

La vue du composant incorpore le PDF via une liaison à la réponse de service

La variable pdfSource ci-dessous est la valeur renvoyée par le service.

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

Voir le Angular documentation DomSanitizer pour plus d'informations.

0
ElliotSchmelliot