How to download a file with Angular

angular download a file
Difficulty

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!

1
1 person likes this.
Please wait...

Leave a Reply

Thanks for choosing to leave a comment.
Please keep in mind that all comments are moderated according to our comment policy, and your email address will NOT be published.
Please do NOT use keywords in the name field. Let's have a personal and meaningful conversation.