This tutorial shows a step by step solution to Angular download file Spring Boot backend. We will cover the download of files located in system and others in classpath.
Table of contents
Server side
This section covers the Spring Boot server implementation
Spring Boot Controller
Our application exposes two endpoints to download CSV files. The first controller downloads files that are located in system and the second from class path. Below the implementation :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package com.roufid.tutorials.controller; import com.roufid.tutorials.service.FileService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletResponse; @RestController public class FileController { private final FileService fileService; @Autowired public FileController(FileService fileService) { this.fileService = fileService; } @GetMapping(value = "/api/files/system/{filename}", produces = "text/csv; charset=utf-8") @ResponseStatus(HttpStatus.OK) public Resource getFileFromFileSystem(@PathVariable String filename, HttpServletResponse response) { return fileService.getFileSystem(filename, response); } @GetMapping(value = "/api/files/classpath/{filename}", produces = "text/csv; charset=utf-8") @ResponseStatus(HttpStatus.OK) public Resource getFileFromClasspath(@PathVariable String filename, HttpServletResponse response) { return fileService.getClassPathFile(filename, response); } } |
- @RestController is used to make the Java Class handle HTTP requests. @RestController is a shortcut of the combination of @Controller along with @ResponseBody.
- The controller handles two GET HTTP requests and produces a text/csv; charset=utf-8 media type and returns response body of type Resource.
- The name of the file to download is given as a @PathVariable
The controller is as simple as you see. Let’s see now the FileService.
Spring Component FileService
We extracted the logic of retrieving the files in a Spring Component FileService. Below it’s content :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package com.roufid.tutorials.service; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletResponse; @Component public class FileService { private enum ResourceType { FILE_SYSTEM, CLASSPATH } private static final String FILE_DIRECTORY = "/var/files"; /** * @param filename filename * @param response Http response. * @return file from system. */ public Resource getFileSystem(String filename, HttpServletResponse response) { return getResource(filename, response, ResourceType.FILE_SYSTEM); } /** * @param filename filename * @param response Http response. * @return file from classpath. */ public Resource getClassPathFile(String filename, HttpServletResponse response) { return getResource(filename, response, ResourceType.CLASSPATH); } private Resource getResource(String filename, HttpServletResponse response, ResourceType resourceType) { response.setContentType("text/csv; charset=utf-8"); response.setHeader("Content-Disposition", "attachment; filename=" + filename); response.setHeader("filename", filename); Resource resource = null; switch (resourceType) { case FILE_SYSTEM: resource = new FileSystemResource(FILE_DIRECTORY + filename); break; case CLASSPATH: resource = new ClassPathResource("data/" + filename); break; } return resource; } } |
Retrieving files is quite simple with Spring. For a file located in system, FileSystemResource is used to wich we pass the absolute path to file. And for a file located in class path, ClassPathResource is used with the absolute path within the class path.
A Content-Disposition header is added to the HTTP response with attachment as value in order to make the file downloadable and savable locally.
That’s it for server side. Let’s see now the front end implementation.
Client side with Angular
I used Angular CLI to create the application. I also use the file-saver NPM library. To install it : npm i --save file-saver
Angular component
For this tutorial, a simple component is created with two filename inputs and two corresponding submit buttons. The first to download file located in file system and the second in class path.
- app.component.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<h1>Roufid tutorials: Angular Spring boot file download</h1> <h2>Download from file system</h2> <form (ngSubmit)="downloadFileSystem()"> <input [(ngModel)]="fileSystemName" name="file-system"> <button [disabled]="!fileSystemName">Download file system</button> </form> <h2>Download from claspath</h2> <form (ngSubmit)="downloadClasspathFile()"> <input [(ngModel)]="classpathFileName" name="classpath-file"> <button [disabled]="!classpathFileName">Download classpath file</button> </form> <router-outlet></router-outlet> |
- app.component.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
import {Component} from '@angular/core'; import * as fileSaver from 'file-saver'; // npm i --save file-saver import {DownloadService} from './services/download.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent { fileSystemName: string; classpathFileName: string; constructor(private downloadService: DownloadService) { } downloadFileSystem() { this.downloadService.downloadFileSystem(this.fileSystemName) .subscribe(response => { const filename = response.headers.get('filename'); this.saveFile(response.body, filename); }); } downloadClasspathFile() { this.downloadService.downloadClasspathFile(this.classpathFileName) .subscribe(response => { const filename = response.headers.get('filename'); this.saveFile(response.body, filename); }); } saveFile(data: any, filename?: string) { const blob = new Blob([data], {type: 'text/csv; charset=utf-8'}); fileSaver.saveAs(blob, filename); } } |
Angular download service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import {HttpClient, HttpHeaders, HttpResponse} from '@angular/common/http'; import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DownloadService { constructor(private http: HttpClient) { } downloadFileSystem(filename: string): Observable<HttpResponse<string>> { let headers = new HttpHeaders(); headers = headers.append('Accept', 'text/csv; charset=utf-8'); return this.http.get('/api/files/system/' + filename, { headers: headers, observe: 'response', responseType: 'text' }); } downloadClasspathFile(filename: string): Observable<HttpResponse<string>> { let headers = new HttpHeaders(); headers = headers.append('Accept', 'text/csv; charset=utf-8'); return this.http.get('/api/files/classpath/' + filename, { headers: headers, observe: 'response', responseType: 'text' }); } } |
The tutorial source code is available on Github. Download the source