In this post, I will show you how to download a file in Angular from a remote web API.

CSV files, Excel spreadsheets, Word documents, PDF reports and images are common types of files that a functional Angular web app may need to download.

In most cases, back-end APIs return files as byte array or blobs in HTTP response body. We will use file-saver package for Angular to download and save a file to the browser.

[toc]

Install packages

Start by getting the file-saver package from npm, along with types:

npm install file-saver
npm install --save-dev @types/file-saverCode language: Bash (bash)

Import file-saver into your component where you want to download a file.

import { saveAs } from 'file-saver';Code language: TypeScript (typescript)

Download file from API

One of the ways you can use file-saver package is to save a blob response:

import { HttpClient } from '@angular/common/http';
import { Component } from '@angular/core';
import { saveAs } from 'file-saver';

@Component({
  selector: 'app-root',
  template: `
  <h1>File download</h1>
  <button (click)="download()">Download</button>
`,
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private http: HttpClient) { }

  download(): void {
    this.http.get<Blob>(`assets/300.png`, {
      responseType: 'blob' as 'json',
    }).subscribe(result => {
      saveAs(result, 'image.jpg');
    });
  }
}

Code language: TypeScript (typescript)

In the example above, we are using the http client to retrieve the file from assets folder. You can change the URL point to a resource in your API. Just make sure that resource returns a blob.

The file should download as image.jpg at the end of the HTTP request.

Note that you must specify the response type explicitly as 'blob' as 'json'.

Download file from link

It is also possible to download a file from a link using file-saver.

The syntax is very easy, just pass the link and specify file name for the file.

downloadFile("https://via.placeholder.com/300.png", "image.jpg");Code language: TypeScript (typescript)

Unit testing file-saver

As always, we need to ensure there is test coverage for file download logic.

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AppComponent } from './app.component';
import * as FileSaver from "file-saver";
import { By } from '@angular/platform-browser';
import { HttpClient } from '@angular/common/http';
import { of } from 'rxjs';

describe('AppComponent', () => {
  let fixture: ComponentFixture<AppComponent>;
  let component: AppComponent;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      providers: [
        {
          provide: HttpClient,
          useValue: jasmine.createSpyObj("HttpClient", ["get"])
        }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;

    const http: any = TestBed.inject(HttpClient);
    http.get.and.returnValue(of(getDummyBlob()));
  });

  it('should create the app', () => {
    expect(component).toBeTruthy();
  });

  it("should save using file saver when download button is clicked", () => {
    const spy = spyOn(FileSaver, "saveAs");
    fixture.debugElement.query(By.css("button")).nativeElement.click();
    expect(spy).toHaveBeenCalledOnceWith(getDummyBlob(), "image.jpg");
  });

  function getDummyBlob(): Blob {
    return new Blob(["1", "2", "2"]);
  }
}); 
Code language: TypeScript (typescript)

The unit test above installs a spy on FileSaver and replicates the saveAs function.

Then, we click on the only button present on the component template to trigger file download.

Finally, our assertion checks that the spy was called once with expected arguments.

API implementation

In case you are downloading a file from your own API, it is important to specify the correct file format.

For example, this is an example implementation in .NET Core Web API:

[HttpPost]
public async Task<IActionResult> Post()
{
  var bytes = _dataExtractServie.ExportToWorkbook();
  return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
}Code language: C# (cs)

In this case, my API is returning an Excel spreadsheet using the spreadsheet format.

Since we’re returning a file response, the same approach can be used to download the spreadsheet:

    this.http.post<Blob>(`api/extract`, {
      responseType: 'blob' as 'json',
    }).subscribe(result => {
      saveAs(result, 'extract.xlsx');
    });Code language: JavaScript (javascript)

Summary

In conclusion, we explored a couple of ways to download a file in Angular with file-saver. What we discussed is applicable to pretty much any front-end framework as file-saver supports HTML5.

Umut Esen

Software Engineer specialising in full-stack web application development.

Leave a Reply

This Post Has One Comment

  1. Noe

    Excelente Post.

    Me salvo la vida