Angular: How to download a file from HttpClient?
Solution 1
Try something like this:
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.');
}
}
Solution 2
Blobs are returned with file type from backend. The following function will accept any file type and popup download window:
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();
}
)
}
Solution 3
It took me a while to implement the other responses, as I'm using Angular 8 (tested up to 10). I ended up with the following code (heavily inspired by Hasan).
Note that for the name to be set, the header Access-Control-Expose-Headers
MUST include Content-Disposition
. To set this in 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"
In 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' })
}
Solution 4
I ended up here when searching for ”rxjs download file using post”.
This was my final product. It uses the file name and type given in the server response.
import { ajax, AjaxResponse } from 'rxjs/ajax';
import { map } from 'rxjs/operators';
downloadPost(url: string, data: any) {
return ajax({
url: url,
method: 'POST',
responseType: 'blob',
body: data,
headers: {
'Content-Type': 'application/json',
'Accept': 'text/plain, */*',
'Cache-Control': 'no-cache',
}
}).pipe(
map(handleDownloadSuccess),
);
}
handleDownloadSuccess(response: AjaxResponse) {
const downloadLink = document.createElement('a');
downloadLink.href = window.URL.createObjectURL(response.response);
const disposition = response.xhr.getResponseHeader('Content-Disposition');
if (disposition) {
const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
const matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
const filename = matches[1].replace(/['"]/g, '');
downloadLink.setAttribute('download', filename);
}
}
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
}
Solution 5
Using Blob out put from API (Excel FIle)
and tweaked @gabrielrincon answer
downloadExcel(): void {
const payload = {
order: 'test',
};
this.service.downloadExcel(payload)
.subscribe((res: any) => {
this.blobToFile(res, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Export.xlsx");
});}
blob to file common function
blobToFile(data: any, type: string, fileName: string) {
const a = document.createElement('a');
document.body.appendChild(a);
a.style.display = 'none';
const blob = new Blob([data], { type: type });
const url = window.URL.createObjectURL(blob);
a.href = url; a.download = fileName; a.click();
window.URL.revokeObjectURL(url);}
in the blob to file function we are expecting first parameter as our blob data, type of file and pass file name including extention 1. we are creating an html a tag element 2. then we append the element in html 3. then hide the a tag element 4. then create new blob object with file and type 5. we will convert the blob object to URL 6. then appened that URL to href property of our a tag 7. we are opening our URL in window so it will download
Comments
-
Jean Carlos almost 2 years
I need download an excel from my backend, its returned a file.
When I do the request I get the error:
TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
My code is:
this.http.get(`${environment.apiUrl}/...`) .subscribe(response => this.downloadFile(response, "application/ms-excel"));
I tried get and map(...) but didn't work.
Details: angular 5.2
references:
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';
Content-Type of response:
Content-Type: application/ms-excel
What's wrong?
-
Plamen over 5 yearsGreat answer!. I would also add "downloadLink.parentNode.removeChild(downloadLink);" after "downloadLink.click();". Just to keep it clear.
-
JoSSte over 4 yearsafter trying several suggestion from this page, a variation of this answer solved my issue
-
bwinchester about 4 yearsThis seems to be the better answer for todays needs and capabilities. It sets the file name using the newer HTML standards. I didn't need the JWT part because we use an intercepter service to add authorization.
-
programmer-man about 4 yearsI got an error using Angular 9: Type '"arraybuffer"' is not assignable to type '"json"'.ts(2322) http.d.ts(1097, 9): The expected type comes from property 'responseType'.
-
Sean Halls about 4 years@programmer-man You now need to do something like: resposeType: 'arrayheaders' as 'json' , see Hasan's answer.
-
gabrielrincon almost 4 yearsI modify the download file and I can set the filename and works perfect me: ` downLoadFile(data: any, type: string) { const fileName = 'file1.xlsx'; const a = document.createElement('a'); document.body.appendChild(a); a.style = 'display: none'; const blob = new Blob([data], {type: type}); const url = window.URL.createObjectURL(blob); a.href = url; a.download = fileName; a.click(); window.URL.revokeObjectURL(url); } `
-
Mojtaba almost 4 yearswhy
'blob' as 'json'
? I think you can only sayresponseType: 'blob'
. -
skilleo over 3 years@Mojtaba cuz typing.
-
Satria over 3 yearsYes, it works and I've been using it for a few weeks now, but I came across the fact, that it only works up to a certain filesize. If the returned data is too large (like 1 - 2 MB), the download window doesn't show up. Plus: Even if it worked, on real big files, you wouldn't see the save dialog until all the data has been received. Not a real download...
-
Eluvatar over 3 yearsthanks, it worked except for the regex, I'm using asp.net and so the content disposition was a bit different, mine looks like this: '/(?:filename=)(.+)(?:;)/'
-
Deepak Dholiyan almost 3 yearsIs this working in Android and Iphone both?
-
Deepak Dholiyan almost 3 yearsOne question:- Why we are not using a tag directly here?
-
cNgamba almost 3 yearsCan you please explain the blobToFile function?
-
Shabeer M almost 3 yearsin the blob to file function we are expecting first parameter as our blob data, type of file and pass file name including extention 1. we are creating an html a tag element 2. then we append the element in html 3. then hide the a tag element 4. then create new blob object with file and type 5. we will convert the blob object to URL 6. then appened that URL to href property of our a tag 7. we are opening our URL in window so it will download
-
cNgamba over 2 yearsThanks. Btw how can I display a message on any error?
-
cNgamba over 2 yearsSay for example if the file is not found I wanted to display a message in my UI
-
Rohit Kumar over 2 yearssorry but it won't work for file type .msg & .eml on edge..... what to do then ?
-
kevin628 over 2 yearsDoes the
a
need to actually be added to the body? I didn'tappendChild
thea
and it worked fine. -
Zarepheth over 2 yearsI needed those bits about
SafeResourceUrl
andbypassSecurityTrustResourceUrl
to resolve problems I was having. -
Matt List almost 2 yearsThanks! Can confirm this also works with Angular 13