When uploading files to Servlet containers, application needs to register a MultipartConfigElement class. But Spring Boot makes thing more easy by configuring it automatically. In this tutorial, we’re gonna look at way to build an Angular 6 App Client to upload/get MultipartFile to/from Spring Boot RestApi Server.
Related posts:
– How to upload MultipartFile with Spring Boot
– MultipartFile – SpringBoot + JQuery Ajax + Bootstrap.
– MultipartFile – SpringBoot + AngularJs + Bootstrap.
I. Technologies
– Angular 6
– Java 1.8
– Spring Boot 2.0.3.RELEASE
– Maven 3.3.9
– Spring Tool Suite 3.9.0.RELEASE
II. Overview
1. Spring Boot Server
– StorageService
helps to init, delete all files, store file, load file
– UploadController
uses StorageService
to provide Rest API: POST a file, GET all files
– application.properties to configure parameters such as MultipartFile max size…
– Spring Boot Starter Web dependency in pom.xml
2. Angular 6 App Client
– upload-file.service
provides methods: push File to Storage and get Files.
– list-upload.component
gets and displays list of Files.
– form-upload.component
helps upload File.
– details-upload.component
is detail for each item in list of Files.
III. Practice
1. Spring Boot Server
1.1 Create Spring Boot project
With Dependency:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
1.2 Create Storage Service for File Systems
Create StorageService with 4 functions:
– public void store(MultipartFile file): save a file
– public Resource loadFile(String filename): load a file
– public void deleteAll(): remove all uploaded files
– public void init(): create upload directory
storage/StorageService.java
package com.javasampleapproach.spring.uploadfiles.storage; import java.io.IOException; import java.net.MalformedURLException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.stereotype.Service; import org.springframework.util.FileSystemUtils; import org.springframework.web.multipart.MultipartFile; @Service public class StorageService { Logger log = LoggerFactory.getLogger(this.getClass().getName()); private final Path rootLocation = Paths.get("upload-dir"); public void store(MultipartFile file) { try { Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename())); } catch (Exception e) { throw new RuntimeException("FAIL!"); } } public Resource loadFile(String filename) { try { Path file = rootLocation.resolve(filename); Resource resource = new UrlResource(file.toUri()); if (resource.exists() || resource.isReadable()) { return resource; } else { throw new RuntimeException("FAIL!"); } } catch (MalformedURLException e) { throw new RuntimeException("FAIL!"); } } public void deleteAll() { FileSystemUtils.deleteRecursively(rootLocation.toFile()); } public void init() { try { Files.createDirectory(rootLocation); } catch (IOException e) { throw new RuntimeException("Could not initialize storage!"); } } }
1.3 Create Upload Controller
controller/UploadController.java
package com.javasampleapproach.spring.uploadfiles.controller; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; import com.javasampleapproach.spring.uploadfiles.storage.StorageService; @Controller public class UploadController { @Autowired StorageService storageService; Listfiles = new ArrayList (); @PostMapping("/post") public ResponseEntity handleFileUpload(@RequestParam("file") MultipartFile file) { String message = ""; try { storageService.store(file); files.add(file.getOriginalFilename()); message = "You successfully uploaded " + file.getOriginalFilename() + "!"; return ResponseEntity.status(HttpStatus.OK).body(message); } catch (Exception e) { message = "FAIL to upload " + file.getOriginalFilename() + "!"; return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message); } } @GetMapping("/getallfiles") public ResponseEntity > getListFiles(Model model) { List
fileNames = files .stream().map(fileName -> MvcUriComponentsBuilder .fromMethodName(UploadController.class, "getFile", fileName).build().toString()) .collect(Collectors.toList()); return ResponseEntity.ok().body(fileNames); } @GetMapping("/files/{filename:.+}") @ResponseBody public ResponseEntity getFile(@PathVariable String filename) { Resource file = storageService.loadFile(filename); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"") .body(file); } }
1.4 Config multipart
Open application.properties:
spring.servlet.multipart.max-file-size=500KB spring.servlet.multipart.max-request-size=500KB
– spring.servlet.multipart.max-file-size
: limit total file size for each request.
– spring.servlet.multipart.max-request-size
: limit total request size for a multipart/form-data.
1.5 Init Storage for File System
SpringBootUploadFileApplication.java
package com.javasampleapproach.spring.uploadfiles; import javax.annotation.Resource; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.javasampleapproach.spring.uploadfiles.storage.StorageService; @SpringBootApplication public class SpringBootFileUploadApplication implements CommandLineRunner { @Resource StorageService storageService; public static void main(String[] args) { SpringApplication.run(SpringBootFileUploadApplication.class, args); } @Override public void run(String... arg) throws Exception { storageService.deleteAll(); storageService.init(); } }
2. Angular 6 App Client
2.0 Generate Service & Components
Run commands below:
– ng g s upload/UploadFile
– ng g c upload/FormUpload
– ng g c upload/ListUpload
– ng g c upload/DetailsUpload
On each Component selector, delete app-
prefix, then change tslint.json rules
– "component-selector"
to false.
2.1 App Module
app.module.ts
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { DetailsUploadComponent } from './upload/details-upload/details-upload.component'; import { FormUploadComponent } from './upload/form-upload/form-upload.component'; import { ListUploadComponent } from './upload/list-upload/list-upload.component'; @NgModule({ declarations: [ AppComponent, DetailsUploadComponent, FormUploadComponent, ListUploadComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
2.2 Upload File Service
upload/upload-file.service.ts
import { Injectable } from '@angular/core'; import { HttpClient, HttpEvent, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UploadFileService { constructor(private http: HttpClient) { } pushFileToStorage(file: File): Observable> { const formdata: FormData = new FormData(); formdata.append('file', file); const req = new HttpRequest('POST', '/post', formdata, { reportProgress: true, responseType: 'text' }); return this.http.request(req); } getFiles(): Observable { return this.http.get('/getallfiles'); } }
2.3 Component for getting List of Files
upload/list-upload.component.ts
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { UploadFileService } from '../upload-file.service'; @Component({ selector: 'list-upload', templateUrl: './list-upload.component.html', styleUrls: ['./list-upload.component.css'] }) export class ListUploadComponent implements OnInit { showFile = false; fileUploads: Observable; constructor(private uploadService: UploadFileService) { } ngOnInit() { } showFiles(enable: boolean) { this.showFile = enable; if (enable) { this.fileUploads = this.uploadService.getFiles(); } } }
upload/list-upload.component.html
<button class="button btn-info" *ngIf='showFile' (click)='showFiles(false)'>Hide Files</button> <button class="button btn-info" *ngIf='!showFile' (click)='showFiles(true)'>Show Files</button> <div [hidden]="!showFile"> <div class="panel panel-primary"> <div class="panel-heading">List of Files</div> <div *ngFor="let file of fileUploads | async"> <div class="panel-body"> <details-upload [fileUpload]='file'></details-upload> </div> </div> </div> </div>
upload/details-upload.component.ts
import { Component, OnInit, Input } from '@angular/core'; @Component({ selector: 'details-upload', templateUrl: './details-upload.component.html', styleUrls: ['./details-upload.component.css'] }) export class DetailsUploadComponent implements OnInit { @Input() fileUpload: string; constructor() { } ngOnInit() { } }
upload/details-upload.component.html
<a href="{{fileUpload}}">{{fileUpload}}</a>
2.4 Component for uploading File
upload/form-upload.component.ts
import { Component, OnInit } from '@angular/core'; import { UploadFileService } from '../upload-file.service'; import { HttpResponse, HttpEventType } from '@angular/common/http'; @Component({ selector: 'form-upload', templateUrl: './form-upload.component.html', styleUrls: ['./form-upload.component.css'] }) export class FormUploadComponent implements OnInit { selectedFiles: FileList; currentFileUpload: File; progress: { percentage: number } = { percentage: 0 }; constructor(private uploadService: UploadFileService) { } ngOnInit() { } selectFile(event) { this.selectedFiles = event.target.files; } upload() { this.progress.percentage = 0; this.currentFileUpload = this.selectedFiles.item(0); this.uploadService.pushFileToStorage(this.currentFileUpload).subscribe(event => { if (event.type === HttpEventType.UploadProgress) { this.progress.percentage = Math.round(100 * event.loaded / event.total); } else if (event instanceof HttpResponse) { console.log('File is completely uploaded!'); } }); this.selectedFiles = undefined; } }
upload/form-upload.component.html
<div *ngIf="currentFileUpload" class="progress"> <div class="progress-bar progress-bar-info progress-bar-striped" role="progressbar" attr.aria-valuenow="{{progress.percentage}}" aria-valuemin="0" aria-valuemax="100" [ngStyle]="{width:progress.percentage+'%'}"> {{progress.percentage}}%</div> </div> <label class="btn btn-default"> <input type="file" (change)="selectFile($event)"> </label> <button class="btn btn-success" [disabled]="!selectedFiles" (click)="upload()">Upload</button>
2.5 App Component
app.component.ts
import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'JavaSampleApproach'; description = 'Angular-SpringBoot Demo'; }
app.component.html
<div class="container" style="width:400px"> <div style="color: blue; margin-bottom: 20px"> <h1>{{title}}</h1> <h3>{{description}}</h3> </div> <form-upload></form-upload> <br/> <br/> <list-upload></list-upload> </div>
2.6 Integrate Spring Boot Server with Angular 6 client
– Create proxy.conf.json file under project:
{ "/": { "target": "http://localhost:8080", "secure": false } }
– Edit package.json file for ‘start’ script:
... "scripts": { "ng": "ng", "start": "ng serve --proxy-config proxy.conf.json", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, ...
3. Check Results
Run Spring Boot Server and Angular 6 Client App (npm start
).
Open Browser with url http://localhost:4200/
.
Upload files and show list of Files:
Inside Spring Project folder, open upload-dir folder:
IV. Sourcecode
– SpringBootFileUpload-Server
– Angular6Upload-Client