There are some methods to download a file with Angular. One is to obtain a link that leads to a base64
downloadable file, usually provided by a service.
The first method is based on the classic but new generation html.
We provide for the generation of the necessary components and services.
In both cases the ingredients of our recipe are:
- 1 template link (
<a>
) - 1 component
- 1 service
Let’s start from the link in our template:
<!-- app.component.html -->
<a [href]="pdfBase64"
[download]="filename"
target="_blank"
*ngIf="pdfBase64">
Download PDF
</a>
And on component:
// app.component.ts
import { Component, OnInit } from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {Subscription} from 'rxjs';
import {MyService} from './services/my.service';
@Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
private sub: Subscription;
public pdfBase64 = null;
public filename = 'aFile.pdf';
constructor(
private myService: MyService,
private sanitizer: DomSanitizer) { }
ngOnInit(): void {
this.getPdf();
}
private getPdf() {
this.sub = this.myService.getPdf().subscribe({
next: res => {
this.pdfBase64 = this.sanitizer.bypassSecurityTrustResourceUrl(
'data:application/pdf;base64,' + res.base64Pdf
);
},
error: err => {
console.error(err);
}
});
}
}
Who calls the following service:
// ./services/my.service.ts
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {HttpClient} from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class MyService {
constructor(
private _http: HttpClient) {}
/**
* Get the PDF file
*/
public getPdf(orderId: string): Observable<object> {
return this._http.get(this.api() + 'pdf/' + orderId);
}
}
Download with file-saver library
The second method is based on a small library called file-saver
to be loaded into package.json
to then go and run some logic in our service.
First of all you have to install this little library in package.json:
// package.json
...
"dependencies": {
...
"file-saver": "^2.0.2"
...
}
...
The logic in the service is as follows:
// ./services/my.service.ts
import { Injectable } from '@angular/core';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {HttpClient} from '@angular/common/http';
import { saveAs } from 'file-saver/dist/FileSaver.min.js';
@Injectable({
providedIn: 'root'
})
export class DownloadService {
constructor(
private _http: HttpClient) {}
public downloadFile(id: number, fileName: string): Promise<any> {
const b64toBlob = (b64Data, contentType = '', sliceSize = 512) => {
const byteCharacters = atob(b64Data);
const byteArrays = [];
for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
const slice = byteCharacters.slice(offset, offset + sliceSize);
const byteNumbers = new Array(slice.length);
for (let i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
return new Blob(byteArrays, { type: contentType });
};
// this._http.get<any>(this.api + 'downloadFile', { params } )
return this._http.get<any>('https://api.example.com/pdf/download')
.toPromise().then(response => {
if (response && response.base64Pdf) {
const blob = b64toBlob(response.base64Pdf, 'application/octet-stream' );
saveAs( blob, fileName );
}
});
}
}
I entered an example service string that you have to modify with yours:
https://api.example.com/pdf/download
With a fake response property: response.base64Pdf
Call it on component:
// app.component.ts
import { Component, OnInit } from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {Subscription} from 'rxjs';
import {MyService} from './services/my.service';
@Component({
selector: 'app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {
private sub: Subscription;
constructor(private myService: MyService) { }
ngOnInit(): void {}
public onDownload(id: string) {
this.loadingDownload = true;
this.myService.downloadFile(id, 'file.pdf').then(
() => {
this.loadingDownload = false;
}
);
}
}
And finally we can conclude in the template:
<!-- app.component.html -->
<a (click)="onDownload()" href="javascript:void(0);">
Download PDF
<span *ngIf="loadingDownload" class="spinner"></span>
</a>
I also added a property for a spinner, giving the “loading” effect.
That’s all.
Try it at home!