Comment télécharger un fichier avec Angular2

j'ai une application WebApi / MVC pour laquelle je développe un client angular2 (pour remplacer MVC). J'ai du mal à comprendre comment Angular sauve un fichier.

la requête est ok (fonctionne très bien avec MVC, et nous pouvons enregistrer les données reçues) mais je ne peux pas comprendre comment enregistrer les données téléchargées (je suis la plupart du temps la même logique que dans ce post ). Je suis sûr que c'est stupidement simple, mais jusqu'à présent je ne suis tout simplement pas saisir.

le code de la fonction composant est ci-dessous. J'ai essayé différentes alternatives, le blob way devrait être le chemin pour aller aussi loin que j'ai compris, mais il n'y a pas de fonction createObjectURL dans URL . Je ne trouve même pas la définition de URL dans window, mais apparemment elle existe. Si j'utilise le FileSaver.js module , j'obtiens la même erreur. Je suppose donc que c'est quelque chose qui a changé récemment ou qui n'est pas encore mis en œuvre. Comment puis-je activer le fichier save dans 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);
}

Par souci d'exhaustivité, le service qui récupère les données ci-dessous, mais la seule chose qu'il fait est d'émettre la demande et de transmettre les données sans cartographie si elle réussit:

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

Note: je sais qu'il ne s'agit pas d'un exemple de travail minimal, mais j'ai consacré beaucoup d'efforts à la description du problème. Si vous allez à downvote veuillez laisser un commentaire.

87
demandé sur Frederik Struck-Schøning 2016-02-01 22:15:37

18 réponses

le problème est que l'observable s'exécute dans un autre contexte, donc quand vous essayez de créer L'URL var vous avez un objet vide et pas le blob que vous voulez.

L'un des nombreux moyens qui existent pour résoudre ce problème , est le suivant:

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

lorsque la requête est prête, elle appellera la fonction "downloadFile" qui est définie comme suit:

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

le blob a été créé parfaitement et donc L'URL var, si n'ouvre pas la nouvelle fenêtre veuillez vérifier que vous avez déjà importé 'rxjs/Rx';

  import 'rxjs/Rx' ;

j'espère que cela peut vous aider.

94
répondu Alejandro Corredor 2018-05-17 12:04:12

Essayer ce !

1 - Installez les dépendances pour montrer enregistrer/ouvrir un fichier pop-up

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

2-Créer un service avec cette fonction pour recevoir les données

downloadFile(id): Observable<Blob> {
    let options = new RequestOptions({responseType: ResponseContentType.Blob });
    return this.http.get(this._baseUrl + '/' + id, options)
        .map(res => res.blob())
        .catch(this.handleError)
}

3-dans le component parse le blob avec 'file-saver'

import {saveAs as importedSaveAs} from "file-saver";

  this.myService.downloadFile(this.id).subscribe(blob => {
            importedSaveAs(blob, this.fileName);
        }
    )

ça marche pour moi!

49
répondu Hector Cuevas 2018-05-17 12:05:11

si vous n'avez pas besoin d'ajouter des en-têtes dans la demande, pour télécharger un fichier dans Angular2 vous pouvez faire un simple:

window.location.href='http://example.com/myuri/report?param=x';

dans votre composant.

14
répondu surfealokesea 2018-05-18 07:13:44

Download *.solution zip Pour l'Angle 2.4.x: vous devez importer Responsabilecontenttype à partir de '@angular/http' et changer responsabiletype en Responsabilecontenttype.ArrayBuffer (par défaut it Responsabilecontenttype.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']);
}
13
répondu Alex Dzeiko 2018-05-17 12:07:01

comme mentionné par Alejandro Corredor il s'agit d'une simple erreur de portée. Le subscribe est exécuté de manière asynchrone et le open doit être placé dans ce contexte, de sorte que le chargement des données soit terminé lorsque nous déclenchons le téléchargement.

cela dit, il y a deux façons de le faire. Comme le recommandent les docs, le service s'occupe d'obtenir et de cartographier les données:

//On the service:
downloadfile(runname: string, type: string){
  var headers = new Headers();
  headers.append('responseType', 'arraybuffer');
  return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .map(res => new Blob([res],{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }))
            .catch(this.logAndPassOn);
}

alors, sur le composant nous nous abonnons et traiter les données mappées. Il y a deux possibilités. La première , comme suggéré dans le post original, mais a besoin d'une petite correction, comme indiqué par Alejandro:

//On the component
downloadfile(type: string){
  this.pservice.downloadfile(this.rundata.name, type)
      .subscribe(data => window.open(window.URL.createObjectURL(data)),
                  error => console.log("Error downloading the file."),
                  () => console.log('Completed file download.'));
  }

la seconde serait D'utiliser FileReader. La logique est la même, mais nous pouvons explicitement attendre que FileReader charge les données, en évitant l'imbrication, et en résolvant le problème asynchrone.

//On the component using FileReader
downloadfile(type: string){
    var reader = new FileReader();
    this.pservice.downloadfile(this.rundata.name, type)
        .subscribe(res => reader.readAsDataURL(res), 
                    error => console.log("Error downloading the file."),
                    () => console.log('Completed file download.'));

    reader.onloadend = function (e) {
        window.open(reader.result, 'Excel', 'width=20,height=10,toolbar=0,menubar=0,scrollbars=no');
  }
}

Note: j'essaie de télécharger un fichier Excel, et même si le téléchargement est déclenché (donc cela répond à la question), le fichier est corrompu. voir la réponse à ce post pour éviter le fichier corrompu.

12
répondu rll 2018-05-17 12:09:24

c'est pour les gens qui cherchent comment le faire en utilisant HttpClient et file-saver:

  1. fichier d'Installation de l'économiseur d'

npm fichier d'installation de l'économiseur d' --save

npm install @types de fichiers/-veille-enregistrer

classe de Service API:

export() {
    return this.http.get(this.download_endpoint, 
        {responseType: 'blob'});
}

composant:

import { saveAs } from 'file-saver';
exportPdf() {
    this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));
}
8
répondu Justin 2018-05-18 07:15:52

téléchargement de fichier par ajax est toujours un processus douloureux et à mon avis, il est préférable de laisser le serveur et le navigateur faire ce travail de négociation de type de contenu.

je pense que c'est mieux d'avoir

<a href="api/sample/download"></a> 

pour le faire. Cela ne nécessite même pas de toute nouvelle ouverture des fenêtres et des trucs comme ça.

le contrôleur MVC comme dans votre échantillon peut être comme celui ci-dessous:

[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()
{
    // ...
    return File(dataStream.ToArray(), "text/plain", "myblob.txt");
}
7
répondu Bharat Raj 2018-08-03 01:24:58

et ça?

this.http.get(targetUrl,{responseType:ResponseContentType.Blob})
        .catch((err)=>{return [do yourself]})
        .subscribe((res:Response)=>{
          var a = document.createElement("a");
          a.href = URL.createObjectURL(res.blob());
          a.download = fileName;
          // start download
          a.click();
        })

ça me ferait du bien.

pas besoin de paquet supplémentaire.

6
répondu harufumi.abe 2018-05-17 12:06:53

pour télécharger et afficher PDF les fichiers, un code très similaire snippé est comme ci-dessous:

  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!")
      );
  }
3
répondu Baatar 2018-05-17 12:05:19

pour ceux utilisant le modèle Redux

j'ai ajouté dans le fichier-épargnant comme @Hector Cuevas nommé dans sa réponse. En utilisant Angular2 v. 2.3.1, Je n'ai pas eu besoin d'ajouter le fichier @types/file-saver.

l'exemple suivant est de télécharger un journal au format PDF.

, La revue des actions

public static DOWNLOAD_JOURNALS = '[Journals] Download as PDF';
public downloadJournals(referenceId: string): Action {
 return {
   type: JournalActions.DOWNLOAD_JOURNALS,
   payload: { referenceId: referenceId }
 };
}

public static DOWNLOAD_JOURNALS_SUCCESS = '[Journals] Download as PDF Success';
public downloadJournalsSuccess(blob: Blob): Action {
 return {
   type: JournalActions.DOWNLOAD_JOURNALS_SUCCESS,
   payload: { blob: blob }
 };
}

, La revue des effets de

@Effect() download$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS)
    .switchMap(({payload}) =>
        this._journalApiService.downloadJournal(payload.referenceId)
        .map((blob) => this._actions.downloadJournalsSuccess(blob))
        .catch((err) => handleError(err, this._actions.downloadJournalsFail(err)))
    );

@Effect() downloadJournalSuccess$ = this.actions$
    .ofType(JournalActions.DOWNLOAD_JOURNALS_SUCCESS)
    .map(({payload}) => saveBlobAs(payload.blob, 'journal.pdf'))

Le journal de bord de

public downloadJournal(referenceId: string): Observable<any> {
    const url = `${this._config.momentumApi}/api/journals/${referenceId}/download`;
    return this._http.getBlob(url);
}

le service HTTP

public getBlob = (url: string): Observable<any> => {
    return this.request({
        method: RequestMethod.Get,
        url: url,
        responseType: ResponseContentType.Blob
    });
};

Le journal réducteur Bien que seul les états corrects utilisés dans notre application, je voulais encore ajouter pour afficher la structure complète.

case JournalActions.DOWNLOAD_JOURNALS: {
  return Object.assign({}, state, <IJournalState>{ downloading: true, hasValidationErrors: false, errors: [] });
}

case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: {
  return Object.assign({}, state, <IJournalState>{ downloading: false, hasValidationErrors: false, errors: [] });
}

j'espère que c'est utile.

3
répondu Casper Nybroe 2018-05-17 12:05:40

je partage la solution qui m'a aidé (toute amélioration est grandement appréciée)

Sur votre service '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'));
}

Composant :

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

sur la partie composante, vous appelez le service sans vous abonner à une réponse. Abonner pour une liste complète des types mime openOffice voir : http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html

3
répondu Ismail H 2018-05-17 12:10:12

pour les nouvelles versions angulaires:

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


import {saveAs} from 'file-saver/FileSaver';

this.http.get('endpoint/', {responseType: "blob", headers: {'Accept': 'application/pdf'}})
  .subscribe(blob => {
    saveAs(blob, 'download.pdf');
  });
3
répondu Tobias Ernst 2018-06-29 15:33:47

J'utilise L'angle 4 avec l'objet 4.3 httpClient. J'ai modifié une réponse que j'ai trouvée dans le Blog technique de Js qui crée un objet lien, l'utilise pour faire le téléchargement, puis le détruit.

Client:

doDownload(id: number, contentType: string) {
    return this.http
        .get(this.downloadUrl + id.toString(), { headers: new HttpHeaders().append('Content-Type', contentType), responseType: 'blob', observe: 'body' })
}

downloadFile(id: number, contentType: string, filename:string)  {

    return this.doDownload(id, contentType).subscribe(  
        res => { 
            var url = window.URL.createObjectURL(res);
            var a = document.createElement('a');
            document.body.appendChild(a);
            a.setAttribute('style', 'display: none');
            a.href = url;
            a.download = filename;
            a.click();
            window.URL.revokeObjectURL(url);
            a.remove(); // remove the element
        }, error => {
            console.log('download error:', JSON.stringify(error));
        }, () => {
            console.log('Completed file download.')
        }); 

} 

la valeur de ceci.downloadUrl a été défini précédemment pour pointer vers l'api. Je l'utilise pour télécharger des pièces jointes, donc je connais l'id, contentType et le nom de fichier: J'utilise une api MVC pour retourner le fichier:

 [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
    public FileContentResult GetAttachment(Int32 attachmentID)
    { 
        Attachment AT = filerep.GetAttachment(attachmentID);            
        if (AT != null)
        {
            return new FileContentResult(AT.FileBytes, AT.ContentType);  
        }
        else
        { 
            return null;
        } 
    } 

la classe de fixation ressemble à ceci:

 public class Attachment
{  
    public Int32 AttachmentID { get; set; }
    public string FileName { get; set; }
    public byte[] FileBytes { get; set; }
    public string ContentType { get; set; } 
}

le dépôt filerep renvoie le fichier de la base de données.

Espérons que cela aide quelqu'un :)

2
répondu davaus 2018-05-18 07:17:11

Salut j'ai la solution pour télécharger à partir d'angular 2 sans se corrompre .... en utilisant le ressort mvc et l'angle 2

1er - mon type de retour est :- ResponseEntity de java fin....ici j'envoie byte [] le tableau a le type de retour du contrôleur.

2ème-pour inclure le filesaver dans votre espace de travail-dans la page d'index comme: -

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

de la 3e à la composante ts écrire ce code:-

      import {ResponseContentType} from '@angular.core';

let headers = new Headers({ 'Content-Type': 'application/json', 'MyApp-Application' : 'AppName', 'Accept': 'application/pdf' });
        let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob });
            this.http
            .post('/project/test/export',
                    somevalue,options)
              .subscribe(data => {

                  var mediaType = 'application/vnd.ms-excel';
                  let blob: Blob = data.blob();
                    window['saveAs'](blob, 'sample.xls');

                });

cela vous donnera le format de fichier xls...si vous voulez d'autres formats, s'il vous plaît changer le mediatype et le nom du fichier avec extension droite...merci j'espère que ça aide....

1
répondu user2025069 2018-05-17 12:06:43

voici quelque chose que j'ai fait dans mon cas -

// service method
downloadFiles(vendorName, fileName) {
    return this.http.get(this.appconstants.filesDownloadUrl, { params: { vendorName: vendorName, fileName: fileName }, responseType: 'arraybuffer' }).map((res: ArrayBuffer) => { return res; })
        .catch((error: any) => _throw('Server error: ' + error));
}

// a controller function which actually downloads the file
saveData(data, fileName) {
    var a = document.createElement("a");
    document.body.appendChild(a);
    a.style = "display: none";
    let blob = new Blob([data], { type: "octet/stream" }),
        url = window.URL.createObjectURL(blob);
    a.href = url;
    a.download = fileName;
    a.click();
    window.URL.revokeObjectURL(url);
}

// a controller function to be called on requesting a download
downloadFiles() {
    this.service.downloadFiles(this.vendorName, this.fileName).subscribe(data => this.saveData(data, this.fileName), error => console.log("Error downloading the file."),
        () => console.info("OK"));
}

la solution est référencée de - ici.

1
répondu Tushar Walzade 2018-10-01 10:07:20
 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();
                });
0
répondu user2025069 2018-08-19 17:20:23

Il sera mieux si vous essayez d'appeler la nouvelle méthode à l'intérieur de vous subscribe

this._reportService.getReport()
    .subscribe((data: any) => {
        this.downloadFile(data);
    },
        (error: any) => onsole.log(error),
        () => console.log('Complete')
    );

à l'Intérieur "151930920 la fonction de" nous avons besoin de faire block, link, href and file name

downloadFile(data: any, type: number, name: string) {
    const blob = new Blob([data], {type: 'text/csv'});
    const dataURL = window.URL.createObjectURL(blob);

    // IE doesn't allow using a blob object directly as link href
    // instead it is necessary to use msSaveOrOpenBlob
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
      window.navigator.msSaveOrOpenBlob(blob);
      return;
    }

    const link = document.createElement('a');
    link.href = dataURL;
    link.download = 'export file.csv';
    link.click();

    setTimeout(() => {

      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(dataURL);
      }, 100);
    }
}
0
répondu Volodymyr Khmil 2018-08-20 11:07:14

mise à Jour d'Hector réponse à l'aide de fichiers de veille et HttpClient pour l'étape 2:

public downloadFile(file: File): Observable<Blob> {
    return this.http.get(file.fullPath, {responseType: 'blob'})
}
0
répondu dmcgrandle 2018-08-23 20:50:56