Come faccio a scaricare un file con Angular2 o versione successiva


182

Ho un'app WebApi / MVC per la quale sto sviluppando un client angular2 (per sostituire MVC). Sto riscontrando dei problemi nel capire come Angular salva un file.

La richiesta è ok (funziona bene con MVC e possiamo registrare i dati ricevuti) ma non riesco a capire come salvare i dati scaricati (sto seguendo principalmente la stessa logica di questo post ). Sono sicuro che è stupidamente semplice, ma finora non lo sto semplicemente afferrando.

Il codice della funzione componente è di seguito. Ho provato diverse alternative, il modo in cui blob dovrebbe essere la strada da percorrere per quanto ho capito, ma non c'è alcuna funzione createObjectURLin URL. Non riesco nemmeno a trovare la definizione di URLin window, ma a quanto pare esiste. Se uso il FileSaver.jsmodulo ottengo lo stesso errore. Quindi suppongo che questo sia qualcosa che è cambiato di recente o non è ancora stato implementato. Come posso attivare il salvataggio del file in 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);
}

Per completezza, il servizio che recupera i dati è riportato di seguito, ma l'unica cosa che fa è emettere la richiesta e trasmettere i dati senza mappatura se riesce:

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

Non è possibile scaricare file di grandi dimensioni con questo metodo. Colpirai il limite di memoria per scheda. Potrebbe arrivare a 1-2 GB.
Matthew B.

@MatthewB. vorrei che avessi detto cosa era meglio.
steve

Per download di file di grandi dimensioni è necessario specificare una nuova scheda, ad esempio se si simula un clic <A>, la destinazione deve essere uguale a "_blank" oppure inviare un modulo. Non credo che ci sia un modo chiaro per aggirare il limite delle dimensioni di file di grandi dimensioni con richieste in stile Ajax.
Matthew B.

Risposte:


181

Il problema è che l'osservabile viene eseguito in un altro contesto, quindi quando si tenta di creare l'URL var, si ha un oggetto vuoto e non il BLOB desiderato.

Uno dei molti modi che esistono per risolvere questo è il seguente:

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

Quando la richiesta è pronta, chiamerà la funzione "downloadFile" che è definita come segue:

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

il BLOB è stato creato perfettamente e quindi l'URL var, se non apre la nuova finestra, controlla di aver già importato 'rxjs / Rx';

  import 'rxjs/Rx' ;

Spero che ciò possa aiutarti.


9
Che cos'è this._reportService.getReport()e cosa restituisce?
Burjua,

3
@Burjua the Returns getReport()athis.http.get(PriceConf.download.url)
ji-ruh,

6
Il problema che sto riscontrando è che la finestra si apre e si chiude immediatamente senza scaricare il file
Braden Brown,

7
Come possiamo impostare il nome del file qui? di default sceglie un valore numerico come nome
Saurabh

8
Ho usato il codice sopra per scaricare un file dalla risposta API ma sto riscontrando un errore nella creazione della parte BLOB "La risposta del tipo non è assegnabile al tipo BLOBpart". Si prega di aiutare se qualcuno conosce questo problema
knbibin,

92

Prova questo !

1 - Installa le dipendenze per il pop-up del file show save / open

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

2- Creare un servizio con questa funzione per ricevere i dati

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- Nel componente analizzare il BLOB con 'file-saver'

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

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

Questo funziona per me!


1
Ho usato il passaggio 2 in combinazione con la risposta di @ Alejandro e ha funzionato senza la necessità di installare il file-saver ...
Ewert

5
Grazie! Funziona perfettamente! Mi chiedo se possiamo ottenere il nome file definito nell'intestazione della risposta. È possibile?
jfajunior,

2
errore Av5 L'argomento di tipo 'RequestOptions' non è assegnabile al parametro di tipo '{headers ?: HttpHeaders | {[header: string]: string | corda[]; };
lavoro il

Questo, tuttavia, non è adatto per il download di file di grandi dimensioni.
Reven,

61

Se non hai bisogno di aggiungere intestazioni nella richiesta, per scaricare un file in Angular2 puoi fare una semplice :

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

nel tuo componente.


4
Qualcuno può dire perché questa risposta è stata sottoposta a downgrade? L'argomento è scaricare un file usando angular2. Se questo metodo funziona per eseguire un download semplice, dovrebbe essere contrassegnato anche come risposta valida.
Saurabh Shetty,

5
@SaurabhShetty, questo non aiuta nel caso in cui si desideri inviare intestazioni personalizzate, e se si desidera inviare un token di autenticazione, ad esempio? Se guardi nella domanda OP puoi vedere che usa authHttp!
A.Akram

6
Comprendo i voti negativi, tuttavia questa risposta ha risolto il mio problema.
JoeriShoeby,

1
Se si lascia che il server restituisca l'URL in qualche contesto, il server potrebbe preparare l'URL. es: oggetto: MyRecord.Cover. La copertina potrebbe essere un URL per un'immagine nel server. Quando si chiama get (Myrecord) si lascia che il server restituisca l'URL preparato (Cover), con token di sicurezza e altre intestazioni impostate.
Jens Alenius,

2
È una risposta che funziona. Solo perché non ha <inserire funzionalità utili qui> che non lo rendono una risposta.
Gburton,

47

Questo è per chi cerca come farlo usando HttpClient e risparmiatore di file:

  1. Installa risparmiatore file

npm install file-saver --save

npm install @ types / file-saver --save

Classe di servizio API:

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

Componente:

import { saveAs } from 'file-saver';
exportPdf() {
    this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`));
}

1
Come mostrare le dimensioni del file nel browser all'avvio del download? Sto inviando la dimensione del file come lunghezza del contenuto nell'intestazione http.
HumbleCoder

35

Cosa ne pensi di questo?

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

Potrei farcela.
non è necessario alcun pacchetto aggiuntivo.


3
Così semplice eppure è quello che funziona perfettamente. Non ingombra il DOM, non crea alcun elemento. Ho combinato questa soluzione con alcuni degli abissi e funziona come un fascino.
Chax,

20

Come menzionato da Alejandro Corredor, si tratta di un semplice errore dell'ambito. Il subscribeviene eseguito in modo asincrono e opendeve essere posto in quel contesto, in modo che i dati finiti caricamento quando si attiverà il download.

Detto questo, ci sono due modi per farlo. Come raccomandato dai documenti, il servizio si occupa di ottenere e mappare i dati:

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

Quindi, sul componente ci limitiamo a sottoscrivere e gestire i dati mappati. Vi sono due possibilità. Il primo , come suggerito nel post originale, ma necessita di una piccola correzione, come osservato da 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.'));
  }

Il secondo modo sarebbe usare FileReader. La logica è la stessa, ma possiamo attendere esplicitamente che FileReader carichi i dati, evitando l'annidamento e risolvendo il problema asincrono.

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

Nota: sto cercando di scaricare un file Excel e anche se il download è attivato (quindi risponde alla domanda), il file è corrotto. Vedi la risposta a questo post per evitare il file corrotto.


7
Penso che il motivo per cui il file viene danneggiato è perché si sta caricando resnel BLOB e in realtà si desidera res._body. Tuttavia _bodyè una variabile privata e non accessibile. Ad oggi .blob()e .arrayBuffer()su un oggetto di risposta http non sono stati implementati in Angular 2. text()e json()sono le uniche due opzioni ma entrambe danneggeranno il tuo corpo. Hai trovato una soluzione?
sschueller,

1
ciao @rll, ho seguito i passaggi precedenti e ricevo l'abbonamento come completato. Non riesco ancora a vedere il file scaricato. Non ho visto alcun errore. Per favore
aiutatemi

1
Le 2 opzioni mi consentono di scaricare il file, ma carica prima i dati in background. Cosa succede se ho un file di grandi dimensioni che deve essere scaricato?
f123,

1
La mia soluzione è usare solo <a href=""></a>per scaricare un file.
user2061057

1
So che questa è una vecchia risposta, ma è in cima ai risultati della ricerca ed è la risposta accettata: la riga `headers.append ('responseType', 'arraybuffer');` è errata. È un'opzione, non un'intestazione. Per favore, aggiustalo. Aaaand ... Le intestazioni vengono create e non utilizzate. Non d'aiuto.
Stevo,

17

Scarica la soluzione * .zip per 2.4.x angolare: devi importare ResponseContentType da '@ angular / http' e modificare responseType in ResponseContentType.ArrayBuffer (per impostazione predefinita 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']);
}

16

Per le versioni angolari più recenti:

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

Grazie, funziona con Angular 8. Non so perché sia ​​stato così difficile da trovare.
MDave,

11

Scaricare file tramite Ajax è sempre un processo doloroso e, a mio avviso, è meglio lasciare che server e browser facciano questo lavoro di negoziazione del tipo di contenuto.

Penso che sia meglio avere

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

per farlo. Ciò non richiede nemmeno l'apertura di nuove finestre e cose del genere.

Il controller MVC come nel tuo esempio può essere come quello qui sotto:

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

1
Hai ragione, ma come puoi gestire gli errori del server all'interno dell'applicazione a pagina singola? In caso di errore, normalmente, un servizio REST restituisce JSON con l'errore, determinando così l'applicazione per aprire JSON in un'altra finestra del browser, che non è ciò che l'utente vuole vedere
Luca

2
Se si dispone di un token di accesso, è necessario fornire che non funziona
chris31389,

Questo è chiaro e semplice. Ma se vuoi fare un po 'di autenticazione, allora c'è la possibilità di avere qualcosa come un token una volta. Quindi, invece di averlo così, puoi avere l'URL come: example.com/myuri/report?tokenid=1234-1233 E verificare l'ID token nel database. Ovviamente non è uno scenario semplice e funziona in tutte le situazioni, ma può essere una soluzione in situazioni in cui, hai accesso al database prima di restituire il rapporto come flusso ..
GingerBeer

Ottieni l'URL di download dal server. In questo modo il server può preparare l'URL con token di sicurezza di una volta.
Jens Alenius,

8

Sto usando Angular 4 con l'oggetto httpClient 4.3. Ho modificato una risposta che ho trovato nel Blog tecnico di Js che crea un oggetto link, lo usa per fare il download, quindi lo distrugge.

Cliente:

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

} 

Il valore di this.downloadUrl è stato impostato in precedenza per puntare all'API. Sto usando questo per scaricare gli allegati, quindi conosco l'id, contentType e il nome del file: sto usando un api MVC per restituire il file:

 [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 degli allegati si presenta così:

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

Il repository filerep restituisce il file dal database.

Spero che questo aiuti qualcuno :)


7

Per quelli che usano Redux Pattern

Ho aggiunto il file-saver come @Hector Cuevas nella sua risposta. Usando Angular2 v. 2.3.1, non ho avuto bisogno di aggiungere il @ types / file-saver.

L'esempio seguente è scaricare un giornale come PDF.

Le azioni del diario

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

Gli effetti del diario

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

Il servizio di giornale

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

Il servizio HTTP

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

Il riduttore di giornale Anche se questo imposta solo gli stati corretti utilizzati nella nostra applicazione, volevo comunque aggiungerlo per mostrare il modello completo.

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

Spero che questo sia utile.


7

Condivido la soluzione che mi ha aiutato (ogni miglioramento è molto apprezzato)

Sul tuo servizio "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'));
}

Componente :

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

Nella parte componente, si chiama il servizio senza sottoscrivere una risposta. L'abbonamento per un elenco completo dei tipi di mime openOffice è disponibile all'indirizzo : http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html


7

Sarà meglio se provi a chiamare il nuovo metodo dentro di te subscribe

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

downloadFile(data)Funzione interna che dobbiamo svolgereblock, 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);
    }
}

5

Per scaricare e mostrare i file PDF , un codice molto simile visualizzato come segue:

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

5

Ecco qualcosa che ho fatto nel mio caso -

// 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 soluzione è referenziata da - qui


4

Aggiorna alla risposta di Hector usando file-saver e HttpClient per il passaggio 2:

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

3

Ho ottenuto una soluzione per il download da Angular 2 senza corrompere, usando Spring Mvc e Angular 2

1 ° il mio tipo di ritorno è: - ResponseEntity da java end. Qui sto inviando array di byte [] ha tipo restituito dal controller.

2 ° - per includere il filesaver nel tuo spazio di lavoro-nella pagina dell'indice come:

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

3 ° al componente ts scrivere questo codice:

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

                });

Questo ti darà il formato di file xls. Se desideri altri formati, modifica il tipo di file multimediale e il nome del file con l'estensione corretta.


3

Oggi stavo affrontando lo stesso caso, ho dovuto scaricare un file pdf come allegato (il file non dovrebbe essere reso nel browser, ma scaricato invece). Per ottenere ciò ho scoperto che dovevo ottenere il file in un Angular Blobe, allo stesso tempo, aggiungere Content-Dispositionun'intestazione nella risposta.

Questo è stato il più semplice che potessi ottenere (Angular 7):

All'interno del servizio:

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

Quindi, quando devo scaricare il file in un componente, posso semplicemente:

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

AGGIORNARE:

Rimossa l'impostazione dell'intestazione non necessaria dal servizio


Se uso window.location.href invece di window.open Chrome lo considera come download di più file.
DanO,

questo non funzionerà se hai il token di autenticazione necessario nell'intestazione
garg10may

3

Il seguente codice ha funzionato per me

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

2

Ho trovato le risposte finora carenti di informazioni e avvertimenti. Potresti e dovresti cercare incompatibilità con IE10 + (se ti interessa).

Questo è l'esempio completo con la parte dell'applicazione e la parte di servizio successive. Si noti che impostiamo la osservata: "response" per catturare l'intestazione del nome file. Si noti inoltre che l'intestazione Content-Disposition deve essere impostata ed esposta dal server, altrimenti l'attuale HttpClient angolare non la trasmetterà. Ho aggiunto un pezzo di codice core dotnet per quello qui sotto.

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

Core Dotnet, con 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.");
        }
    }

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

1

Basta mettere il urlcome hrefdi seguito.

<a href="my_url">Download File</a>

Funziona? Ottengo errore ... "ERRORE TypeError:" Accesso allo 'file: ///Downloads/test.json' dallo script negato. ""
Jay

Grazie puoi condividere come appare il tuo url? Id it protocollo di file o http o qualcos'altro?
Jay,

È il protocollo di file.
Harunur Rashid,


1

Puoi anche scaricare un file direttamente dal tuo modello in cui usi l'attributo download e [attr.href]puoi fornire un valore di proprietà dal componente. Questa semplice soluzione dovrebbe funzionare sulla maggior parte dei browser.

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

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


1
Benvenuti in SO! Verifica se le mie correzioni (di composizione e grammatica) sono utili.
B - rian,

0

Se invii i parametri solo a un URL, puoi farlo in questo modo:

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

nel servizio che riceve i parametri


0

Questa risposta suggerisce che non è possibile scaricare file direttamente con AJAX, principalmente per motivi di sicurezza. Quindi descriverò cosa faccio in questa situazione,

01. Aggiungi hrefattributo nel tuo tag di ancoraggio all'interno del component.htmlfile,
ad esempio: -

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

02. Esegui tutti i seguenti passaggi component.tsper ignorare il livello di sicurezza e visualizzare la finestra di dialogo popup come salvataggio,
ad es .: -

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

Nota: questa risposta funzionerà se si riceve un errore "OK" con il codice di stato 200


0

Bene, ho scritto un pezzo di codice ispirato a molte delle risposte di cui sopra che dovrebbe funzionare facilmente nella maggior parte degli scenari in cui il server invia un file con un'intestazione di disposizione del contenuto, senza installazioni di terze parti, ad eccezione di rxjs e angolare.

Innanzitutto, come chiamare il codice dal tuo file componente

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

Come puoi vedere, è praticamente la chiamata back-end media da angolare, con due modifiche

  1. Sto osservando la risposta invece del corpo
  2. Sono esplicito che la risposta è un blob

Una volta che il file viene recuperato dal server, in linea di principio, sto delegando l'intera attività di salvataggio del file nella funzione di supporto, che tengo in un file separato, e l'importazione in qualsiasi componente che devo

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

Non ci sono più nomi di file GUID criptici! Possiamo usare qualsiasi nome fornito dal server, senza doverlo specificare esplicitamente nel client, oppure sovrascrivere il nome file fornito dal server (come in questo esempio). Inoltre, si può facilmente, se necessario, modificare l'algoritmo di estrazione del nome file dalla disposizione del contenuto per soddisfare le loro esigenze e tutto il resto rimarrà inalterato - in caso di errore durante tale estrazione, passerà semplicemente 'null' come nome del file.

Come già sottolineato un'altra risposta, IE ha bisogno di un trattamento speciale, come sempre. Ma con il bordo del cromo in arrivo tra pochi mesi, non me ne preoccuperei mentre costruisco nuove app (si spera). C'è anche la questione di revocare l'URL, ma non ne sono così sicuro, quindi se qualcuno potesse darci una mano nei commenti, sarebbe fantastico.


0

Se una scheda si apre e si chiude senza scaricare nulla, ho provato a seguire con un finto collegamento di ancoraggio e ha funzionato.

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);  }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.