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 11 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.
Contents
I. Technologies
– Angular 11
– 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 11 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;
List files = 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 11 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 11 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 11 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