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.
Table of contents
Install packages
Start by getting the file-saver
package from npm, along with types:
npm install file-saver
npm install --save-dev @types/file-saver
Code 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.
Excelente Post.
Me salvo la vida