Today, I faced a requirement where I need to implement file save functionality in an Angular 4 application. In this quick post, I will show you how to implement this functionality in Angular 4 using FileSaver.js module.
Step 1: Create an Angular 4 project
I use Angular CLI to generate Angular 4 applications. Navigate to a convenient location on your file system and run the following command.
$ ng new file-save-ng4-example $ cd file-save-ng4-example $ npm install # yarn install
$is used to denote bash prompt. You don’t have to type it on your command-line terminal.
Step 2: Install FileSaver.js dependency
FileSaver.js is an HTML 5 FileSaver functionality. It makes it dead simple to implement file save functionality. FileSaver.js is available as a npm package so you can install it as shown below.
$ npm install --save file-saver
Step 3: Implement file save functionality in frontend
To showcase how to implement file save functionality, we will add a button to AppComponent. When we click on the button then we will make a REST API call to fetch the content.
Let’s start by updating app.component.html to as shown below.
<div style="text-align:center"> <h1> Export by clicking button below</h1> <button (click)="saveFile()">Export</button></div>
In the code snippet above, we added a button with click event handled. The saveFile function will exist inside the app.component.ts as shown below.
import { Component } from '@angular/core';
import { Http, Headers } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { saveAs } from 'file-saver/FileSaver';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private http: Http) { }
saveFile() {
const headers = new Headers();
headers.append('Accept', 'text/plain');
this.http.get('/api/files', { headers: headers })
.toPromise()
.then(response => this.saveToFileSystem(response));
}
private saveToFileSystem(response) {
const contentDispositionHeader: string = response.headers.get('Content-Disposition');
const parts: string[] = contentDispositionHeader.split(';');
const filename = parts[1].split('=')[1];
const blob = new Blob([response._body], { type: 'text/plain' });
saveAs(blob, filename);
}
}
In the code shown above, we did following:
- We implemented
saveFilefunction inAppComponent. It makes HTTP get request to our service. - Next, we converted
ObservabletoPromiseusingrxjstoPromiseoperator. - Next, we read
Content-Dispositionresponse header to find the filename. - Next, we constructed
Blobobject using the response body. - Finally, we passed blob to FileSaver.js saveAs functionality. It take care of the rest of functionality.
It might happen that
response.headers.get('Content-Disposition')return value asnulleven though you are setting it in the response header on server side. It wasted a lot of time to figure out that Angular 4 does not set headers until you setAccess-Control-Expose-Headersheader in the server side code. Refer to step 4 where I show a sample Java Spring Boot REST end point.
If you run the code now, you will get 404 as service does not exist. To show a full working example, next step covers shows a Spring Boot application that implements the API.
Step 5: Implement REST API
I use Spring Initializer project to scaffold a Spring Boot project. Once you have created that, just replace your application class with the one shown below.
package com.example.filesavebackend;
import java.util.Collections;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/api/**")
.allowedOrigins("http://localhost:4200")
.allowedMethods("*");
}
};
}
}
@RestController
class FileResource {
@GetMapping(path = "/api/files", produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity getFile() {
String exportedContent = "Hello, World!";
String filename = "my-file.txt";
HttpHeaders headers = new HttpHeaders();
headers.setAccessControlExposeHeaders(Collections.singletonList("Content-Disposition"));
headers.set("Content-Disposition", "attachment; filename=" + filename);
return new ResponseEntity<>(exportedContent, headers, HttpStatus.OK);
}
}
The important thing to note is FileResource. The getFile method implements our REST API. It sets content and required headers. Please note that we are explicitly setting Access-Control-Expose-Headers header. If you don’t set it, then you will not be able to access Content-Disposition header in Angular.
Source code of the full application
You can get the source code of the full application from Github: shekhargulati/filesave-angular4-example
Discover more from Shekhar Gulati
Subscribe to get the latest posts sent to your email.
Getting this error..
1. Error: No provider for Http!
at injectionError (core.es5.js:1169)
at noProviderError (core.es5.js:1207) etc
2. Unhandled Promise rejection: No provider for Http! ; Zone: ; Task: Promise.then ; Value: Error: No provider for Http!
at injectionError (core.es5.js:1169)
at noProviderError (core.es5.js:1207)
Import the HTTP module in the app.module.ts as done in the Github repository https://github.com/shekhargulati/filesave-angular4-example/blob/master/angular-frontend/src/app/app.module.ts#L13.
While implementing saveAs(blob, fileName) getting error as
location-list.service.ts (77,5): Cannot find name ‘saveAs’. and one more error is
Argument of type ‘{ headers: void; }’ is not assignable to parameter of type ‘RequestOptionsArgs’.
while using the following line in my code
const headers = new Headers();
headers.append(‘Accept’, ‘text/plain’);
this.http.get(‘/api/files’, { headers: headers })
Please provide me help.
i am implementing the above code in Angular4 application
I need to change the downloaded file from download folder to some project folder.
How to implement that?
Error: Cannot find module ‘@angular-devkit/core’
at Function.Module._resolveFilename (module.js:555:15)
at Function.Module._load (module.js:482:25)
at Module.require (module.js:604:17)
at require (internal/module.js:11:18)
at Object. (C:\Users\arun.prabhu\Downloads\filesave-angular4-example-master\filesave-angular4-example-master\angular-frontend\node_modules\@angular-devkit\schematics\src\tree\virtual.js:10:16)
at Module._compile (module.js:660:30)
at Object.Module._extensions..js (module.js:671:10)
at Module.load (module.js:573:32)
at tryModuleLoad (module.js:513:12)
at Function.Module._load (module.js:505:3)
try to run the below command in the project folder:
npm install @angular-devkit/core –save-dev
Hi sir, can you please write a post for Implementing file save functionality with Angular and backend asp.net services.