Frage Wie lade ich eine Datei mit Angular2 herunter?


Ich habe eine WebApi / MVC-App, für die ich einen angular2-Client entwickle (um MVC zu ersetzen). Ich habe 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 (ich folge meistens der gleichen Logik wie in dieser Beitrag). Ich bin mir sicher, dass das dumm ist, aber ich verstehe es einfach nicht.

Der Code der Komponentenfunktion ist unten. Ich habe verschiedene Alternativen ausprobiert, der Blob-Weg sollte der Weg sein, so weit ich es verstanden habe, aber es gibt keine Funktion createObjectURL im URL. Ich kann nicht einmal die Definition von finden URL im Fenster, aber anscheinend existiert es. Wenn ich das benutze FileSaver.js Modul Ich bekomme den gleichen Fehler. Ich denke, das ist etwas, das sich kürzlich geändert hat oder noch nicht umgesetzt wurde. Wie kann ich die 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, aber das einzige, was er tut, ist die Ausgabe der Anfrage und die Weitergabe der Daten ohne Zuordnung, wenn es erfolgreich ist:

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

Hinweis: Ich weiß, dass dies kein minimales Arbeitsbeispiel ist, aber ich habe mich sehr um die Problembeschreibung bemüht. Wenn Sie einen Kommentar abgeben möchten, hinterlassen Sie bitte einen Kommentar.


75
2018-02-01 19:15


Ursprung


Antworten:


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 das gewünschte Blob.

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

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){
  var blob = new Blob([data], { type: 'text/csv' });
  var url= window.URL.createObjectURL(blob);
  window.open(url);
}

der Blob wurde perfekt erstellt und so die URL var, wenn das neue Fenster nicht geöffnet wird, bitte überprüfen Sie, ob Sie bereits 'rxjs / Rx' importiert haben;

  import 'rxjs/Rx' ;

hoffe das kann dir helfen.


79
2018-02-04 19:54



Versuchen Dies!

1 - Installieren Sie die Abhängigkeiten für das Popup-Fenster zum Speichern / Öffnen von Dateien

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

2- Erstellen Sie einen Service mit dieser Funktion, um die Daten zu erhalten

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- In der Komponente parsen Sie 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 für mich!


47
2018-05-11 10:24



Download * .zip-Lösung für eckige 2.4.x: Sie müssen ResponseContentType von '@ angular / 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']);
}

12
2018-02-12 14:58



Wie erwähnt von Alejandro Corredor Es ist ein einfacher Bereich Fehler. Das subscribe wird asynchron ausgeführt und die open muss in diesem Kontext platziert werden, damit die Daten beim Laden des Downloads vollständig geladen werden.

Allerdings gibt es zwei Möglichkeiten, dies zu tun. Wie die Dokumente empfehlen, kümmert sich der Dienst darum, die Daten abzurufen und zuzuordnen:

//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 auf der Komponente, die wir gerade abonnieren und mit den zugeordneten Daten umgehen. Es gibt zwei Möglichkeiten. Der Erste, wie in der ursprünglichen Post vorgeschlagen, aber benötigt eine kleine Korrektur wie von Alejandro bemerkt:

//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, FileReader zu verwenden. Die Logik ist die gleiche, 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 (damit die Frage beantwortet wird), ist die Datei beschädigt. Siehe die Antwort zu diesem Beitrag zum Vermeiden Sie die beschädigte Datei.


11
2018-02-05 15:35



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

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

in deiner Komponente.


11
2017-08-04 05:37



Dies ist für Leute, die schauen, wie es mit HttpClient und file-saver funktioniert:

  1. Installieren Sie den Dateischoner

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

8
2018-01-29 14:13



Das Herunterladen von Dateien über Ajax ist immer ein mühsamer Prozess und meiner Ansicht nach ist es am besten, wenn Server und Browser diese Arbeit der Aushandlung von Inhaltstypen ausführen.

Ich denke, es ist das Beste zu haben

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

es zu tun. Das erfordert nicht einmal neue Fenster und solche Sachen.

Der MVC-Controller wie in Ihrer Probe kann wie folgt aussehen:

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

7
2018-01-20 05:13



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 nötig.


4
2017-07-13 00:28



Für diejenigen, die Redux Pattern verwenden

Ich habe den Dateischoner als @Hector Cuevas in seiner Antwort hinzugefügt. 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 Zeitschrift wirkt

@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 Journal Reducer Obwohl dies nur die korrekten Zustände in unserer Anwendung festlegt, wollte ich es trotzdem hinzufügen, um das vollständige Muster zu zeigen.

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.


3
2017-08-17 09:01



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

Auf Ihrem Bedienung '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'));
}

Komponente Teil:

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 dem Bauteil rufen Sie den Service auf, ohne eine Antwort zu abonnieren. Das abonnieren Eine vollständige Liste der OpenOffice-MIME-Typen finden Sie unter: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html 


3
2018-01-18 14:16