web-dev-qa-db-fra.com

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 des difficultés à comprendre comment Angular enregistre un fichier.

La demande est ok (fonctionne bien avec MVC, et nous pouvons enregistrer les données reçues), mais je ne sais pas comment enregistrer les données téléchargées (je suis principalement dans la même logique que dans cet article ). Je suis sûr que c'est bêtement simple, mais jusqu'ici je ne le comprends tout simplement pas.

Le code de la fonction composant est ci-dessous. J'ai essayé différentes alternatives, la manière de blob devrait être la voie à suivre pour autant que je sache, mais il n'y a pas de fonction createObjectURL dans URL. Je ne trouve même pas la définition de URL dans la fenêtre, mais apparemment, elle existe. Si j'utilise le module FileSaver.js j'obtiens la même erreur. Donc, je suppose que c'est quelque chose qui a changé récemment ou qui n'est pas encore implémenté. Comment puis-je déclencher l'enregistrement du fichier en 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 se trouve en dessous, mais il ne fait qu'émettre la demande et transmettre les données sans mappage s'il réussit:

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

Le problème est que l'observable s'exécute dans un autre contexte. Ainsi, lorsque vous essayez de créer l'URL var, vous avez un objet vide et non le blob que vous souhaitez.

Une des nombreuses façons de résoudre ce problème est la suivante:

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

Lorsque la demande est prête, la fonction "downloadFile" sera définie comme suit:

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

le blob a été créé parfaitement et donc l'URL var, si ne s'ouvre pas, vérifiez que vous avez déjà importé 'rxjs/Rx';

  import 'rxjs/Rx' ;

J'espère que cela pourra vous aider.

115
Alejandro Corredor

Essayez this !

1 - Installer les dépendances pour afficher le fichier de sauvegarde/ouvrir un fichier contextuel

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

2- Créez 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 composant, analysez le blob avec 'file-saver'

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

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

Cela fonctionne pour moi!

62
Hector Cuevas

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 effectuer une simple:

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

dans votre composant.

27
surfealokesea

Comme mentionné par Alejandro Corredor c'est une simple erreur de champ d'application. La subscribe est exécutée de manière asynchrone et la open doit être placée dans ce contexte pour que le chargement des données se termine lorsque nous déclenchons le téléchargement.

Cela dit, il y a deux façons de le faire. Comme le recommande la documentation, le service s'occupe de récupérer et de mapper 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);
}

Ensuite, nous nous abonnons simplement au composant et traitons les données mappées. Il y a deux possibilités. Le premier, comme suggéré dans le message d'origine, mais nécessite une petite correction, comme l'a noté 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 deuxième} 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 message for en évitant le fichier corrompu.

17
rll

C'est pour les gens qui cherchent comment faire avec HttpClient et File-Saver

  1. Installer un économiseur de fichier

npm install file-saver --save

npm install @ types/fichier-économiseur --save

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`));
}
17
Justin

Téléchargez la solution * .Zip pour angular 2.4.x: vous devez importer ResponseContentType à partir de '@ angular/http' et changer responseType en ResponseContentType.ArrayBuffer (par défaut, 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']);
}
15
Alex Dzeiko

Que dis-tu de ç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();
        })

Je pourrais faire avec ça.
pas besoin de paquet supplémentaire.

14
harufumi.abe

Télécharger un fichier via ajax est toujours un processus difficile et, à mon avis, il est préférable de laisser le serveur et le navigateur effectuer ce travail de négociation du 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 l'ouverture de nouvelles fenêtres et ce genre de choses.

Le contrôleur MVC, comme dans votre exemple, peut ressembler à celui ci-dessous:

[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()
{
    // ...
    return File(dataStream.ToArray(), "text/plain", "myblob.txt");
}
9
Bharat Raj

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');
  });
8
Tobias Ernst

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 partie:

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 composant, vous appelez le service sans vous abonner à une réponse. Pour vous procurer une liste complète des types de mime openOffice, voir: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html

5
Ismail H

Pour ceux qui utilisent Redux Pattern

J'ai ajouté dans l'économiseur de fichier en tant que @Hector Cuevas nommé dans sa réponse. En utilisant Angular2 v. 2.3.1, je n’avais pas besoin d’ajouter dans @ types/fichier-économiseur.

L'exemple suivant consiste à télécharger un journal au format PDF.

Les actions du journal

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

Les effets de journal

@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 service

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 réducteur de journal Bien que cela ne définisse que les états corrects utilisés dans notre application, je souhaitais quand même l'ajouter pour montrer le motif complet.

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 ceci est utile.

5
Casper Nybroe

Pour télécharger et afficherPDFfichiers, un extrait de code très similaire se présente comme suit:

  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!")
      );
  }
4
Baatar

J'utilise Angular 4 avec l'objet 4.3 httpClient. J'ai modifié une réponse trouvée dans le blog technique de Js, qui crée un objet de lien, l'utilise pour effectuer 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 this.downloadUrl a été définie précédemment pour pointer sur l'api. J'utilise ceci pour télécharger des pièces jointes, je connais donc l'id, le contentType et le nom du 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 pièce jointe 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 référentiel filerep renvoie le fichier à partir de la base de données.

J'espère que ça aide quelqu'un :)

4
davaus

Effectuez la mise à jour de la réponse d'Hector à l'aide de File-Saver et de HttpClient à l'étape 2:

public downloadFile(file: File): Observable<Blob> {
    return this.http.get(file.fullPath, {responseType: 'blob'})
}
3
dmcgrandle

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 - here

3
Tushar Walzade

J'ai eu une solution pour télécharger de angular 2 sans être corrompu, En utilisant spring mvc et angular 2

1er- mon type de retour est: - ResponseEntity de Java end. Ici, j'envoie un octet []. Le tableau a le type de retour du contrôleur.

2ème pour inclure l'économiseur de fichiers dans votre espace de travail dans la page d'index en tant que:

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

Le troisième composant enregistre 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 souhaitez utiliser d'autres formats, modifiez le type de média et le nom du fichier avec l'extension appropriée.

2
user2025069

Ce 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')
    );

Dans la fonction downloadFile(data), nous devons créer 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);
    }
}
1
Volodymyr Khmil

Vous pouvez également télécharger un fichier directement à partir de votre modèle où vous utilisez l'attribut download et à [attr.href] vous pouvez fournir une valeur de propriété à partir du composant. Cette solution simple devrait fonctionner sur la plupart des navigateurs.

<a download [attr.href]="yourDownloadLink"></a>

Référence: https://www.w3schools.com/tags/att_a_download.asp

0
Max

<a href="my_url" download="myfilename">Download file</a>

my_url devrait avoir la même origine, sinon il sera redirigé vers cet emplacement

0
VIVEK SINGH

Il suffit de mettre url comme href comme ci-dessous 

<a href="my_url">Download File</a>
0
Harunur Rashid

Je faisais face au même cas aujourd'hui, je devais télécharger un fichier pdf en pièce jointe (le fichier ne devrait pas être rendu dans le navigateur, mais téléchargé à la place). Pour y parvenir, j'ai découvert que je devais obtenir le fichier dans un Angular Blob et, en même temps, ajouter un en-tête Content-Disposition dans la réponse.

C'était le plus simple que j'ai pu obtenir (Angular 7):

À l'intérieur du service:

getFile(id: String): Observable<HttpResponse<Blob>> {
  return this.http.get(`./file/${id}`, {responseType: 'blob', observe: 'response'});
}

Ensuite, lorsque je dois télécharger le fichier dans un composant, je peux simplement:

fileService.getFile('123').subscribe((file: HttpResponse<Blob>) => window.location.href = file.url);

METTRE À JOUR:

Suppression des paramètres d'en-tête inutiles du service

0
 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
user2025069