Upload files to Servlet containers, application need register a MultipartConfigElement class. But Spring Boot makes it more easy by configuring it automatically. In this tutorial, we’re gonna look at way to build an Angular 5 App Client to upload/get Images to/from Spring Boot RestApi Server.
Related posts:
– How to upload MultipartFile with Spring Boot
– MultipartFile – SpringBoot + JQuery Ajax + Bootstrap.
– MultipartFile – SpringBoot + AngularJs + Bootstrap.
– Angular 4 Firebase – Get List Files from Storage
– Angular 5 – Upload/Get MultipartFile to/from Spring Boot Server
Contents
I. Technologies
– Angular 5
– Java 1.8
– Spring Boot 1.5.7.RELEASE
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.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 5 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:
org.springframework.boot spring-boot-starter-web
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
package com.javasampleapproach.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
package com.javasampleapproach.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.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.max-file
Open application.properties:
spring.http.multipart.max-file-size=500KB spring.http.multipart.max-request-size=500KB
– spring.http.multipart.max-file-size: limit total file size for each request.
– spring.http.multipart.max-request-size: limit total request size for a multipart/form-data.
1.5 Init Storage for File System
package com.javasampleapproach.uploadfiles; import javax.annotation.Resource; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import com.javasampleapproach.uploadfiles.storage.StorageService; @SpringBootApplication public class SpringBootUploadFileApplication implements CommandLineRunner { @Resource StorageService storageService; public static void main(String[] args) { SpringApplication.run(SpringBootUploadFileApplication.class, args); } @Override public void run(String... arg) throws Exception { storageService.deleteAll(); storageService.init(); } }
2. Angular 5 App Client
2.1 App Module
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HttpClientModule } from '@angular/common/http'; import { AppComponent } from './app.component'; import { ListUploadComponent } from './upload/list-upload/list-upload.component'; import { FormUploadComponent } from './upload/form-upload/form-upload.component'; import { DetailsUploadComponent } from './upload/details-upload/details-upload.component'; import { UploadFileService } from './upload/upload-file.service'; @NgModule({ declarations: [ AppComponent, ListUploadComponent, FormUploadComponent, DetailsUploadComponent ], imports: [ BrowserModule, HttpClientModule ], providers: [UploadFileService], bootstrap: [AppComponent] }) export class AppModule { }
2.2 Upload File Service
import { Injectable } from '@angular/core'; import { HttpClient, HttpRequest, HttpEvent } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; @Injectable() 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 Images
list-upload.component.ts
import { Component, OnInit } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; 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(); } } }
list-upload.component.html
List of Images
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() { } }
details-upload.component.html
2.4 Component for uploading Image
form-upload.component.ts
import { Component, OnInit } from '@angular/core'; import { HttpClient, HttpResponse, HttpEventType } from '@angular/common/http'; import { UploadFileService } from '../upload-file.service'; @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) { const file = event.target.files.item(0); if (file.type.match('image.*')) { this.selectedFiles = event.target.files; } else { alert('invalid format!'); } } 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; } }
form-upload.component.html
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
{{title}}
{{description}}
2.6 Integrate Spring Boot Server with Angular 5 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 5 Client App, go to http://localhost:4200/
:
Upload files and show list of Files:
IV. Sourcecode
– SpringBootUploadFile
– Angular5Upload-Images
An interesting thing about Angular is that uploading files is barely handled by anyone. It’s not mentioned in the docs and the first thing you find by using Google is a third party lib, ng2-file-upload .
Hi,
I am getting this error “Response for preflight has invalid HTTP status code 403.”
I replicated the same example.
I dont know what I m missing.
Hi,
It was resolved by adding @CorsOrigin in controller.
Thanks
“timestamp”: “2018-07-06T15:43:00.228+0000”,
“status”: 400,
“error”: “Bad Request”,
“message”: “Required request part ‘file’ is not present”,
bonjour je vient de suivre votre tutoriel mais j’ai cette erreur
me too
footzjcusqwfdadszeicmrqwokwnvp