typescript - Angular: Hvordan laste ned en fil fra HttpClient?
Jeg trenger å laste ned en Excel fra backenden min, den returnerte en fil.
Når jeg utfører forespørselen, får jeg feilen:
TypeError: Du oppga 'undefined' der en strøm ble forventet. Du kan oppgi en Observable, Promise, Array eller Iterable.
Koden min er:
this.http.get(`${environment.apiUrl}/...`)
.subscribe(response => this.downloadFile(response, "application/ms-excel"));
Jeg prøvde å bruke get og map(...) men det fungerte ikke.
Detaljer: angular 5.2
referanser:
import { HttpClient } from '@angular/common/http';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/finally';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/catch';
Innholdstype for respons:
Content-Type: application/ms-excel
Hva er galt?
Svar #1
Blobs returneres med filtype fra backend. Følgende funksjon vil akseptere hvilken som helst filtype og åpne et nedlastingsvindu:
downloadFile(route: string, filename: string = null): void{
const baseUrl = 'http://myserver/index.php/api';
const token = 'my JWT';
const headers = new HttpHeaders().set('authorization','Bearer '+token);
this.http.get(baseUrl + route,{headers, responseType: 'blob' as 'json'}).subscribe(
(response: any) =>{
let dataType = response.type;
let binaryData = [];
binaryData.push(response);
let downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, {type: dataType}));
if (filename)
downloadLink.setAttribute('download', filename);
document.body.appendChild(downloadLink);
downloadLink.click();
}
)
}
Svar #2
Prøv noe som dette:
type: application/ms-excel
/**
* used to get file from server
*/
this.http.get(`${environment.apiUrl}`,{
responseType: 'arraybuffer',headers:headers}
).subscribe(response => this.downLoadFile(response, "application/ms-excel"));
/**
* Method is use to download file.
* @param data - Array Buffer data
* @param type - type of the document.
*/
downLoadFile(data: any, type: string) {
let blob = new Blob([data], { type: type});
let url = window.URL.createObjectURL(blob);
let pwa = window.open(url);
if (!pwa || pwa.closed || typeof pwa.closed == 'undefined') {
alert( 'Please disable your Pop-up blocker and try again.');
}
}
Svar #3
Det tok meg en stund å implementere de andre svarene, siden jeg bruker Angular 8 (testet opp til 13). Jeg endte opp med følgende kode (sterkt inspirert av Hasan).
Merk at for at navnet skal settes, MÅ headeren Access-Control-Expose-Headers inkludere Content-Disposition. For å sette dette i Django RF:
http_response = HttpResponse(package, content_type='application/javascript')
http_response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
http_response['Access-Control-Expose-Headers'] = "Content-Disposition"
I Angular:
// component.ts
// getFileName not necessary, you can just set this as a string if you wish
getFileName(response: HttpResponse<Blob>) {
let filename: string;
try {
const contentDisposition: string = response.headers.get('content-disposition');
const r = /(?:filename=")(.+)(?:;")/
filename = r.exec(contentDisposition)[1];
}
catch (e) {
filename = 'myfile.txt'
}
return filename
}
downloadFile() {
this._fileService.downloadFile(this.file.uuid)
.subscribe(
(response: HttpResponse<Blob>) => {
let filename: string = this.getFileName(response)
let binaryData = [];
binaryData.push(response.body);
let downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: 'blob' }));
downloadLink.setAttribute('download', filename);
document.body.appendChild(downloadLink);
downloadLink.click();
}
)
}
// service.ts
downloadFile(uuid: string) {
return this._http.get<Blob>(`${environment.apiUrl}/api/v1/file/${uuid}/package/`, { observe: 'response', responseType: 'blob' as 'json' })
}
