Wie lade ich eine Datei mit Angular2 herunter?

180

Ich habe eine WebApi / MVC-App, für die ich einen angle2-Client entwickle (als Ersatz für MVC). Ich habe einige Probleme zu verstehen, wie Angular eine Datei speichert.

Die Anfrage ist in Ordnung (funktioniert gut mit MVC und wir können die empfangenen Daten protokollieren), aber ich kann nicht herausfinden, wie die heruntergeladenen Daten gespeichert werden sollen (ich folge größtenteils der gleichen Logik wie in diesem Beitrag ). Ich bin sicher, dass es dumm einfach ist, aber bis jetzt verstehe ich es einfach nicht.

Der Code der Komponentenfunktion ist unten. Ich habe verschiedene Alternativen ausprobiert, der Blob-Weg sollte der Weg sein, soweit ich verstanden habe, aber es gibt keine Funktion createObjectURLin URL. Ich kann nicht einmal die Definition von URLin window finden, aber anscheinend existiert sie. Wenn ich das Modul benutzeFileSaver.js erhalte ich den gleichen Fehler. Ich denke, dies hat sich kürzlich geändert oder ist noch nicht implementiert. Wie kann ich das Speichern der Datei in A2 auslösen?

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

Der Vollständigkeit halber ist der Dienst, der die Daten abruft, unten aufgeführt. Das einzige, was er tut, ist, die Anforderung auszugeben und die Daten ohne Zuordnung weiterzuleiten, wenn dies erfolgreich ist:

downloadfile(runname: string, type: string){
   return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type)
            .catch(this.logAndPassOn);
}
rll
quelle
Mit dieser Methode können Sie keine großen Dateien herunterladen. Sie werden das Speicherlimit pro Registerkarte erreichen. Dies kann nur 1-2 GB betragen.
Matthew B.
@ MatthewB. Ich wünschte, Sie hätten gesagt, was besser wäre.
Steve
Für das Herunterladen großer Dateien müssen Sie eine neue Registerkarte angeben, z. B. wenn Sie einen <A> -Klick simulieren, muss das Ziel gleich "_blank" sein oder ein Formular senden. Ich glaube nicht, dass es einen sauberen Weg gibt, um die Beschränkung der großen Dateigröße mit Ajax-Anfragen zu umgehen.
Matthew B.

Antworten:

177

Das Problem ist, dass das Observable in einem anderen Kontext ausgeführt wird. Wenn Sie also versuchen, die URL var zu erstellen, haben Sie ein leeres Objekt und nicht den gewünschten Blob.

Eine der vielen Möglichkeiten, dies zu lösen, ist folgende:

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

Wenn die Anfrage fertig ist, ruft sie die Funktion "downloadFile" auf, die wie folgt definiert ist:

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

Der Blob wurde perfekt erstellt und daher die URL var. Wenn das neue Fenster nicht geöffnet wird, überprüfen Sie bitte, ob Sie 'rxjs / Rx' bereits importiert haben.

  import 'rxjs/Rx' ;

Ich hoffe das kann dir helfen.

Alejandro Corredor
quelle
9
Was ist this._reportService.getReport()und was gibt es zurück?
Burjua
3
@ Burjua die getReport()Rückkehr athis.http.get(PriceConf.download.url)
ji-ruh
6
Das Problem, das ich habe, ist, dass das Fenster sofort geöffnet und geschlossen wird, ohne die Datei herunterzuladen
Braden Brown
7
Wie können wir hier den Dateinamen einstellen? Standardmäßig wählt es einen numerischen Wert als Namen
Saurabh
8
Ich habe den obigen Code zum Herunterladen einer Datei von der API-Antwort verwendet, aber beim Erstellen des Blob-Teils "Typantwort kann nicht dem Typ Blobpart zugewiesen werden" wird eine Fehlermeldung angezeigt. Bitte helfen Sie, wenn jemand dieses Problem kennt
Knbibin
89

Versuchen Sie dies !

1 - Installieren Sie Abhängigkeiten für das Popup "Datei speichern / öffnen"

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

2- Erstellen Sie mit dieser Funktion einen Dienst, um die Daten zu empfangen

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- Analysieren Sie in der Komponente den Blob mit 'file-saver'.

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

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

Das funktioniert bei mir!

Hector Cuevas
quelle
1
Ich habe Schritt 2 in Kombination mit der Antwort von @Alejandro verwendet und es funktionierte ohne die Notwendigkeit, File-Saver zu installieren ...
Ewert
5
Danke dir! Es funktioniert perfekt! Ich frage mich, ob wir den Dateinamen erhalten können, der in der Kopfzeile der Antwort definiert ist. Ist das möglich?
jfajunior
2
Fehler Av5 Das Argument vom Typ 'RequestOptions' kann dem Parameter vom Typ '{headers?: HttpHeaders | nicht zugewiesen werden {[header: string]: string | string []; };
GiveJob
Dieser ist jedoch nicht zum Herunterladen großer Dateien geeignet.
Reven
60

Wenn Sie der Anforderung keine Header hinzufügen müssen , können Sie zum Herunterladen einer Datei in Angular2 Folgendes einfach tun :

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

in Ihrer Komponente.

surfealokesea
quelle
4
Kann jemand bitte sagen, warum diese Antwort abgelehnt wird? Das Thema ist das Herunterladen einer Datei mit angle2. Wenn diese Methode für einen einfachen Download funktioniert, sollte sie auch als gültige Antwort markiert werden.
Saurabh Shetty
5
@SaurabhShetty, Dies hilft nicht, wenn Sie benutzerdefinierte Header senden möchten. Was ist, wenn Sie beispielsweise ein Authentifizierungstoken senden möchten? Wenn Sie sich die OP-Frage ansehen, können Sie sehen, dass er sie verwendet authHttp!
A.Akram
6
Ich verstehe die Abstimmungen, aber diese Antwort hat mein Problem gelöst.
JoeriShoeby
1
Wenn Sie den Server die URL in einem bestimmten Kontext zurückgeben lassen, kann der Server die URL vorbereiten. Beispiel: Objekt: MyRecord.Cover. Das Cover kann eine URL zu einem Bild auf dem Server sein. Wenn Sie get (Myrecord) aufrufen, lassen Sie den Server die vorbereitete URL (Cover) mit gesetztem Sicherheitstoken und anderen Headern zurückgeben.
Jens Alenius
2
Es ist eine Antwort, die funktioniert. Nur weil es keine <nützliche Funktion hier einfügen> hat, die es nicht zu einer Antwort macht.
Gburton
46

Dies ist für Leute, die suchen, wie es mit HttpClient und File-Saver geht:

  1. Installieren Sie den Dateisparer

npm install file-saver --save

npm install @ types / file-saver --save

API-Serviceklasse:

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

Komponente:

import { saveAs } from 'file-saver';
exportPdf() {
    this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));
}
Justin
quelle
1
Wie wird die Dateigröße im Browser angezeigt, wenn der Download beginnt? Ich sende die Dateigröße als Inhaltslänge im http-Header.
bescheideneCoder
35

Wie wäre es damit?

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

Ich könnte damit anfangen.
Kein zusätzliches Paket erforderlich.

harufumi.abe
quelle
3
So einfach und doch funktioniert es einwandfrei. Das DOM wird nicht überladen, es wird kein Element erstellt. Ich habe diese Lösung mit einigen der oben genannten kombiniert und sie funktioniert wie ein Zauber.
Chax
20

Wie von Alejandro Corredor erwähnt, handelt es sich um einen einfachen Bereichsfehler. Das subscribewird asynchron ausgeführt und dasopen muss in diesen Kontext gestellt werden, damit die Daten beim Auslösen des Downloads vollständig geladen werden.

Es gibt jedoch zwei Möglichkeiten. Wie in den Dokumenten empfohlen, kümmert sich der Dienst um das Abrufen und Zuordnen der Daten:

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

Dann abonnieren wir für die Komponente einfach die zugeordneten Daten und bearbeiten sie. Es gibt zwei Möglichkeiten. Die erste , wie im ursprünglichen Beitrag vorgeschlagen, muss jedoch geringfügig korrigiert werden, wie von Alejandro festgestellt:

//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.'));
  }

Der zweite Weg wäre die Verwendung von FileReader. Die Logik ist dieselbe, aber wir können explizit darauf warten, dass FileReader die Daten lädt, die Verschachtelung vermeidet und das asynchrone Problem löst.

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

Hinweis: Ich versuche, eine Excel-Datei herunterzuladen, und obwohl der Download ausgelöst wird (dies beantwortet die Frage), ist die Datei beschädigt. Siehe die Antwort auf diesen Beitrag, um die beschädigte Datei zu vermeiden.

rll
quelle
7
Ich denke, der Grund, warum die Datei beschädigt wird, ist, dass Sie resin den Blob laden und tatsächlich wollen res._body. Ist _bodyjedoch eine private Variable und nicht zugänglich. Ab heute .blob()und .arrayBuffer()auf einem http - Antwort - Objekt nicht in Angular 2. umgesetzt worden text()und json()sind die einzigen zwei Möglichkeiten , aber beide werden Ihren Körper verstümmeln. Haben Sie eine Lösung gefunden?
Sschueller
1
hi @rll, ich habe die obigen Schritte befolgt und werde als abgeschlossen abonniert. Trotzdem konnte ich nicht sehen, dass die Datei heruntergeladen wurde. Ich konnte auch keinen Fehler sehen. Bitte helfen Sie
AishApp
1
Mit den beiden Optionen kann ich die Datei herunterladen, aber zuerst werden die Daten im Hintergrund geladen. Was ist, wenn ich eine große Datei habe, die heruntergeladen werden muss?
f123
1
Meine Lösung besteht darin, nur <a href=""></a>eine Datei herunterzuladen.
user2061057
1
Ich weiß, dass dies eine alte Antwort ist, aber sie steht ganz oben in den Suchergebnissen und ist die akzeptierte Antwort: Die Zeile `headers.append ('responseType', 'arraybuffer');` ist falsch. Es ist eine Option, kein Header. Bitte repariere es. Aaaand ... Die Header werden erstellt und nicht verwendet. Nicht hilfreich.
Stevo
17

Download * .zip-Lösung für Angular 2.4.x: Sie müssen ResponseContentType aus '@ angle / http' importieren und responseType in ResponseContentType.ArrayBuffer ändern (standardmäßig 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']);
}
Alex Dzeiko
quelle
16

Für neuere Winkelversionen:

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');
  });
Tobias Ernst
quelle
Danke, funktioniert mit Angular 8. Ich weiß nicht, warum das so schwer zu finden war.
MDave
11

Das Herunterladen von Dateien über Ajax ist immer ein schmerzhafter Prozess. Meiner Ansicht nach ist es am besten, Server und Browser diese Arbeit der Aushandlung von Inhaltstypen ausführen zu lassen.

Ich denke, es ist das Beste zu haben

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

es zu tun. Dies erfordert nicht einmal das Öffnen neuer Fenster und ähnliches.

Der MVC-Controller wie in Ihrem Beispiel kann wie folgt aussehen:

[HttpGet("[action]")]
public async Task<FileContentResult> DownloadFile()
{
    // ...
    return File(dataStream.ToArray(), "text/plain", "myblob.txt");
}
Ingwer Bier
quelle
1
Sie haben Recht, aber wie können Sie dann Serverfehler in der einseitigen Anwendung verwalten? Im Fehlerfall gibt normalerweise ein REST-Service den JSON mit dem Fehler zurück, sodass die Anwendung den JSON in einem anderen Browserfenster öffnet, was der Benutzer nicht sehen möchte
Luca
2
Wenn Sie ein Zugriffstoken haben, das Sie bereitstellen müssen, funktioniert dies nicht
chris31389
Das ist schlicht und einfach. Wenn Sie jedoch eine Authentifizierung durchführen möchten, besteht die Möglichkeit, dass Sie so etwas wie ein einmaliges Token haben. Anstatt es so zu haben, können Sie die URL wie folgt verwenden: example.com/myuri/report?tokenid=1234-1233 Und die Token-ID in der Datenbank überprüfen. Natürlich ist es kein einfaches Szenario und funktioniert in allen Situationen, kann aber eine Lösung sein, wenn Sie Zugriff auf die Datenbank haben, bevor Sie den Bericht als Stream zurückgeben.
GingerBeer
Holen Sie sich die Download-URL vom Server. So kann der Server die URL mit einem einmaligen Sicherheitstoken vorbereiten.
Jens Alenius
7

Für diejenigen, die Redux Pattern verwenden

Ich habe im Dateisparer @Hector Cuevas hinzugefügt, der in seiner Antwort genannt wird. Mit Angular2 v. 2.3.1 musste ich den @ types / file-saver nicht hinzufügen.

Im folgenden Beispiel wird ein Journal als PDF heruntergeladen.

Die Journalaktionen

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

Die Journaleffekte

@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'))

Der Journalservice

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

Der HTTP-Dienst

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

Der Journalreduzierer Obwohl dies nur die korrekten Zustände festlegt, die in unserer Anwendung verwendet werden, wollte ich ihn dennoch hinzufügen, um das vollständige Muster anzuzeigen.

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: [] });
}

Ich hoffe das ist hilfreich.

Casper Nybroe
quelle
7

Ich teile die Lösung, die mir geholfen hat (jede Verbesserung wird sehr geschätzt)

In Ihrem Dienst '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'));
}

Bestandteil :

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

Auf der Komponente rufen Sie den Dienst auf, ohne eine Antwort zu abonnieren. Das Abonnement für eine vollständige Liste der openOffice-MIME-Typen finden Sie unter: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html

Ismail H.
quelle
7

Ich verwende Angular 4 mit dem 4.3 httpClient-Objekt. Ich habe eine Antwort geändert, die ich in Js 'technischem Blog gefunden habe. Sie erstellt ein Linkobjekt, verwendet es zum Herunterladen und zerstört es dann.

Klient:

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

} 

Der Wert von this.downloadUrl wurde zuvor so festgelegt, dass er auf die API zeigt. Ich verwende dies zum Herunterladen von Anhängen, daher kenne ich die ID, den Inhaltstyp und den Dateinamen: Ich verwende eine MVC-API, um die Datei zurückzugeben:

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

Die Anhangsklasse sieht folgendermaßen aus:

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

Das Filerep-Repository gibt die Datei aus der Datenbank zurück.

Hoffe das hilft jemandem :)

Davaus
quelle
7

Es ist besser, wenn Sie versuchen, die neue Methode in Ihnen aufzurufen subscribe

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

Innerhalb der downloadFile(data)Funktion müssen wir machenblock, 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);
    }
}
Volodymyr Khmil
quelle
5

Um PDF- Dateien herunterzuladen und anzuzeigen, sieht ein sehr ähnlicher Code wie folgt aus:

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

Hier ist etwas, was ich in meinem Fall getan habe -

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

Die Lösung wird mit Bezug auf den - hier

Tushar Walzade
quelle
4

Aktualisieren Sie die Antwort von Hector mit File-Saver und HttpClient für Schritt 2:

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

Ich habe eine Lösung zum Herunterladen von Angular 2, ohne beschädigt zu werden, mit Spring MVC und Angular 2

1. Mein Rückgabetyp ist: - ResponseEntity vom Java-Ende. Hier sende ich Byte [] Array hat Rückgabetyp von der Steuerung.

2. Um den Dateisparer in Ihren Arbeitsbereich aufzunehmen - auf der Indexseite wie folgt:

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

3. Bei Komponente ts schreiben Sie diesen 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');

                });

Dadurch erhalten Sie das XLS-Dateiformat. Wenn Sie andere Formate wünschen, ändern Sie den Medientyp und den Dateinamen mit der richtigen Erweiterung.

user2025069
quelle
3

Ich war heute mit dem gleichen Fall konfrontiert. Ich musste eine PDF-Datei als Anhang herunterladen (die Datei sollte nicht im Browser gerendert, sondern stattdessen heruntergeladen werden). Um dies zu erreichen, musste ich die Datei in einem Winkel abrufen Blobund gleichzeitig eine hinzufügenContent-Disposition Header in die Antwort einfügen.

Dies war das einfachste, was ich bekommen konnte (Winkel 7):

Innerhalb des Dienstes:

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

Wenn ich dann die Datei in einer Komponente herunterladen muss, kann ich einfach:

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

AKTUALISIEREN:

Unnötige Header-Einstellungen aus dem Dienst entfernt

Gabriel Sezefredo
quelle
Wenn ich window.location.href anstelle von window.open verwende, behandelt Chrome dies als Download mehrerer Dateien.
DanO
Dies wird nicht funktionieren, wenn Sie Auth-Token im Header benötigt haben
garg10may
2

Ich fand die Antworten bisher ohne Einsicht und Warnungen. Sie könnten und sollten auf Inkompatibilitäten mit IE10 + achten (wenn Sie sich interessieren).

Dies ist das vollständige Beispiel mit dem Anwendungsteil und dem Serviceteil danach. Beachten Sie, dass wir die Beobachtung: "Antwort" setzen , um den Header für den Dateinamen abzufangen. Beachten Sie auch, dass der Content-Disposition-Header vom Server festgelegt und verfügbar gemacht werden muss, da er sonst vom aktuellen Angular HttpClient nicht weitergegeben wird. Ich habe unten einen Dotnet-Kerncode hinzugefügt .

public exportAsExcelFile(dataId: InputData) {
    return this.http.get(this.apiUrl + `event/export/${event.id}`, {
        responseType: "blob",
        observe: "response"
    }).pipe(
        tap(response => {
            this.downloadFile(response.body, this.parseFilename(response.headers.get('Content-Disposition')));
        })
    );
}

private downloadFile(data: Blob, filename: string) {
    const blob = new Blob([data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8;'});
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(blob, filename);
    } else {
        const link = document.createElement('a');
        if (link.download !== undefined) {
            // Browsers that support HTML5 download attribute
            const url = URL.createObjectURL(blob);
            link.setAttribute('href', url);
            link.setAttribute('download', filename);
            link.style.visibility = 'hidden';
            document.body.appendChild(link);
            link.click();
            document.body.removeChild(link);
        }
    }
}

private parseFilename(contentDisposition): string {
    if (!contentDisposition) return null;
    let matches = /filename="(.*?)"/g.exec(contentDisposition);

    return matches && matches.length > 1 ? matches[1] : null;
}

Dotnet-Kern mit Content-Disposition & MediaType

 private object ConvertFileResponse(ExcelOutputDto excelOutput)
    {
        if (excelOutput != null)
        {
            ContentDisposition contentDisposition = new ContentDisposition
            {
                FileName = excelOutput.FileName.Contains(_excelExportService.XlsxExtension) ? excelOutput.FileName : "TeamsiteExport.xlsx",
                Inline = false
            };
            Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
            Response.Headers.Add("Content-Disposition", contentDisposition.ToString());
            return File(excelOutput.ExcelSheet, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        }
        else
        {
            throw new UserFriendlyException("The excel output was empty due to no events.");
        }
    }
David Zwart
quelle
1
 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();
                });
user2025069
quelle
1

Setzen die einfach urlso , hrefwie unten.

<a href="my_url">Download File</a>
Harunur Rashid
quelle
Funktioniert es? Ich erhalte eine Fehlermeldung ... "ERROR TypeError:" Zugriff auf 'file: ///Downloads/test.json' aus Skript verweigert. ""
Jay
Vielen Dank, können Sie uns mitteilen, wie Ihre URL aussieht? Id es Dateiprotokoll oder http oder etwas anderes?
Jay
Es ist das Dateiprotokoll.
Harunur Rashid
1

Sie können eine Datei auch direkt aus Ihrer Vorlage herunterladen, in der Sie das Download-Attribut verwenden, und [attr.href]einen Eigenschaftswert aus der Komponente bereitstellen. Diese einfache Lösung sollte in den meisten Browsern funktionieren.

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

Referenz: https://www.w3schools.com/tags/att_a_download.asp

Max
quelle
1
Willkommen bei SO! Bitte überprüfen Sie, ob meine (Satz- und Grammatik-) Korrekturen hilfreich sind.
B - rian
1

Der folgende Code hat bei mir funktioniert

let link = document.createElement('a');
link.href = data.fileurl; //data is object received as response
link.download = data.fileurl.substr(data.fileurl.lastIndexOf('/') + 1);
link.click();
Basil Mohammed
quelle
0

Wenn Sie die Parameter nur an eine URL senden, können Sie dies folgendermaßen tun:

downloadfile(runname: string, type: string): string {
   return window.location.href = `${this.files_api + this.title +"/"+ runname + "/?file="+ type}`;
}

in dem Dienst, der die Parameter empfängt

Joel Sulca Cordova
quelle
0

Dies Antwort legt nahe, dass Sie Dateien nicht direkt mit AJAX herunterladen können, hauptsächlich aus Sicherheitsgründen. Also werde ich beschreiben, was ich in dieser Situation mache,

01. Fügen Sie ein hrefAttribut in Ihr Ankertag in der component.htmlDatei ein,
z. B.: -

<div>
       <a [href]="fileUrl" mat-raised-button (click)='getGenaratedLetterTemplate(element)'> GENARATE </a>
</div>

02. Führen Sie alle folgenden Schritte aus component.ts, um die Sicherheitsstufe zu umgehen und das Dialogfeld Als Popup speichern aufzurufen,
z. B.: -

import { environment } from 'environments/environment';
import { DomSanitizer } from '@angular/platform-browser';
export class ViewHrApprovalComponent implements OnInit {
private apiUrl = environment.apiUrl;
  fileUrl
 constructor(
    private sanitizer: DomSanitizer,
    private letterService: LetterService) {}
getGenaratedLetterTemplate(letter) {

    this.data.getGenaratedLetterTemplate(letter.letterId).subscribe(
      // cannot download files directly with AJAX, primarily for security reasons);
    console.log(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
    this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.apiUrl + 'getGeneratedLetter/' + letter.letterId);
  }

Hinweis: Diese Antwort funktioniert, wenn Sie den Fehler "OK" mit dem Statuscode 200 erhalten

Indrajith Ekanayake
quelle
0

Nun, ich habe einen Code geschrieben, der von vielen der oben genannten Antworten inspiriert ist und in den meisten Szenarien, in denen der Server eine Datei mit einem Header für die Inhaltsdisposition sendet, ohne Installationen von Drittanbietern, außer rxjs und angle, problemlos funktionieren sollte.

Zunächst, wie Sie den Code aus Ihrer Komponentendatei aufrufen

this.httpclient.get(
   `${myBackend}`,
   {
      observe: 'response',
      responseType: 'blob'
   }
).pipe(first())
.subscribe(response => SaveFileResponse(response, 'Custom File Name.extension'));

Wie Sie sehen können, handelt es sich im Grunde genommen um den durchschnittlichen Backend-Aufruf von Angular mit zwei Änderungen

  1. Ich beobachte die Reaktion anstelle des Körpers
  2. Ich spreche ausdrücklich davon, dass die Antwort ein Blob ist

Sobald die Datei vom Server abgerufen wurde, delegiere ich im Prinzip die gesamte Aufgabe des Speicherns der Datei an die Hilfsfunktion, die ich in einer separaten Datei aufbewahre, und importiere sie in die gewünschte Komponente

export const SaveFileResponse = 
(response: HttpResponse<Blob>, 
 filename: string = null) => 
{
    //null-checks, just because :P
    if (response == null || response.body == null)
        return;

    let serverProvidesName: boolean = true;
    if (filename != null)
        serverProvidesName = false;

    //assuming the header is something like
    //content-disposition: attachment; filename=TestDownload.xlsx; filename*=UTF-8''TestDownload.xlsx
    if (serverProvidesName)
        try {
            let f: string = response.headers.get('content-disposition').split(';')[1];
            if (f.includes('filename='))
                filename = f.substring(10);
        }
        catch { }
    SaveFile(response.body, filename);
}

//Create an anchor element, attach file to it, and
//programmatically click it. 
export const SaveFile = (blobfile: Blob, filename: string = null) => {
    const a = document.createElement('a');
    a.href = window.URL.createObjectURL(blobfile);
    a.download = filename;
    a.click();
}

Es gibt keine kryptischen GUID-Dateinamen mehr! Wir können jeden Namen verwenden, den der Server bereitstellt, ohne ihn explizit im Client angeben zu müssen, oder den vom Server bereitgestellten Dateinamen überschreiben (wie in diesem Beispiel). Außerdem kann man den Algorithmus zum Extrahieren des Dateinamens aus der Inhaltsdisposition bei Bedarf leicht ändern, um sie an ihre Bedürfnisse anzupassen, und alles andere bleibt unberührt - im Falle eines Fehlers während einer solchen Extraktion wird nur "null" übergeben. als Dateiname.

Wie bereits in einer anderen Antwort erwähnt, muss der IE wie immer speziell behandelt werden. Aber da Chrom Edge in ein paar Monaten kommt, würde ich mir darüber keine Sorgen machen, wenn ich (hoffentlich) neue Apps erstelle. Es gibt auch die Frage, die URL zu widerrufen, aber da bin ich mir nicht so sicher. Wenn also jemand in den Kommentaren dabei helfen könnte, wäre das großartig.

Dibyodyuti Mondal
quelle
0

Wenn eine Registerkarte geöffnet und geschlossen wird, ohne etwas herunterzuladen, habe ich versucht, mit einem Scheinanker-Link zu folgen, und es hat funktioniert.

downloadFile(x: any) {
var newBlob = new Blob([x], { type: "application/octet-stream" });

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

    // For other browsers: 
    // Create a link pointing to the ObjectURL containing the blob.
    const data = window.URL.createObjectURL(newBlob);

    var link = document.createElement('a');
    link.href = data;
    link.download = "mapped.xlsx";
    // this is necessary as link.click() does not work on the latest firefox
    link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));

    setTimeout(function () {
      // For Firefox it is necessary to delay revoking the ObjectURL
      window.URL.revokeObjectURL(data);
      link.remove();
    }, 100);  }
Damitha
quelle