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 4 App Client to upload/get Images to/from Spring Boot RestApi Server.
Updated Post:
Angular 5 – Upload/Get Images to/from Spring Boot 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 4 – Upload/Get MultipartFile to/from Spring Boot Server
Contents
I. Technologies
– Angular 4
– 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 4 App Client
– upload-file.service
provides methods: push Image File to Storage and get Image Files.
– list-upload.component
gets and displays list of Images.
– form-upload.component
helps upload Image File.
– details-upload.component
is detail for each item in list of Images.
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
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; List<String> files = new ArrayList<String>(); @PostMapping("/post") public ResponseEntity<String> 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<List<String>> getListFiles(Model model) { List<String> 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<Resource> 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 4 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 {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'; import {UploadFileService} from './upload/upload-file.service'; @NgModule({ declarations: [ AppComponent, DetailsUploadComponent, FormUploadComponent, ListUploadComponent ], 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<HttpEvent<{}>> { let 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<string[]> { 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<string[]> constructor(private uploadService: UploadFileService) {} ngOnInit() { } showFiles(enable: boolean) { this.showFile = enable if (enable) { this.fileUploads = this.uploadService.getFiles(); } } } |
list-upload.component.html
<button class="button btn-info" *ngIf='showFile' (click)='showFiles(false)'>Hide Images</button> <button class="button btn-info" *ngIf='!showFile' (click)='showFiles(true)'>Show Images</button> <div [hidden]="!showFile"> <div class="panel panel-primary"> <div class="panel-heading">List of Images</div> <div *ngFor="let file of fileUploads | async"> <div class="panel-body"> <details-upload [fileUpload]='file'></details-upload> </div> </div> </div> </div> |
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
<img src="{{fileUpload}}" alt="{{fileUpload}}" style="max-width:350px"/> |
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
<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 = 'Angular4-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 4 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 4 Client App, go to http://localhost:4200/
:
Upload files and show list of Files:
IV. Sourcecode
– SpringBootUploadFile
– Angular4Upload-Images
Last updated on March 30, 2018.
Nice article. Covers all required details for understanding as well as implementation.
Good effort . I want to disappear progress bar after completion of image uploading . how can be do it .
I am confused a little bit about FileSystemUtils.deleteRecursively(rootLocation.toFile()); and init() as well . is that are using for case of duplication or overwriting image/files in directory .? if not then what is purpose of using them . and can we use it in production environment with aspect of performance ? .
Hi waqas_kamran,
We use them in case we want to clear all historic data.
Depending on your purpose, you can use or not.
Regards,
JSA.
HI There,
It is really very nice article but i have some issue that I am not able to solve. Pls slove it.
When I run this project it showing some error on browser console.
GET
http://localhost:4200/getallfiles
404 (Not Found) zone.js:2933HttpErrorResponse {headers: HttpHeaders, status: 404, statusText: “Not Found”, url: ttp://localhost:4200/getallfiles”, ok: false, …} core.es5.js:1020
The error because of the spring boot application didn’t config CORS and the Angular 4 app send to 4200 port so you should change port to 8080. Once you fixed two issues it will working good.
I tried the above steps in my application to upload a file. springboot works fine when i test it from a postman or restclient. but from angular i get below idea. Unable to figure out how to fix it can you help please?
SyntaxError: Unexpected token b in JSON at position 0
at JSON.parse ()
at JwtInterceptor.intercept (jwt.interceptor.ts:9)
at HttpInterceptorHandler.handle (http.js:1796)
at HttpXsrfInterceptor.intercept (http.js:2489)
at HttpInterceptorHandler.handle (http.js:1796)
at MergeMapSubscriber.eval [as project] (http.js:1466)
at MergeMapSubscriber._tryNext (mergeMap.js:128)
at MergeMapSubscriber._next (mergeMap.js:118)
at MergeMapSubscriber.Subscriber.next (Subscriber.js:92)
at ScalarObservable._subscribe (ScalarObservable.js:51)
View_FormUploadComponent_0 @ FormUploadComponent.html:13
proxyClass @ compiler.js:14652
DebugContext_.logError @ core.js:14979
ErrorHandler.handleError @ core.js:1501
dispatchEvent @ core.js:9949
(anonymous) @ core.js:10570
(anonymous) @ platform-browser.js:2628
ZoneDelegate.invokeTask @ zone.js:421
onInvokeTask @ core.js:4724
ZoneDelegate.invokeTask @ zone.js:420
Zone.runTask @ zone.js:188
ZoneTask.invokeTask @ zone.js:496
invokeTask @ zone.js:1517
globalZoneAwareCallback @ zone.js:1543
FormUploadComponent.html:13 ERROR CONTEXT
Everything works nicely except for the max-file-size in application.properties. I tried to set spring.servlet.multipart.max-request-size to -1 and spring.http.multipart.max-request-size to -1. Neither worked when uploading a file of size 3.4MB
whoa, I’m having the same issue! Wonder if someone has looked into this already. I’ve been stumped for days.
Hi, how do I fix this error.
ERROR in src/app/upload/upload-file.service.ts:24:5 – error TS2322: Type ‘Observable’ is not assignable to type ‘Observable’.
Type ‘Object’ is not assignable to type ‘string’.
24 return this.http.get(‘/getallfiles’);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/app/upload/list-upload/list-upload.component.ts:25:7 – error TS2322: Type ‘Observable’ is not assignable to type ‘Observable’.
Type ‘string’ is not assignable to type ‘string[]’.
25 this.fileUploads = this.uploadService.getFiles();
~~~~~~~~~~~~~~~~
ERROR in src/app/upload/upload-file.service.ts:24:5 – error TS2322: Type ‘Observable’ is not assignable to type ‘Observable’.
The ‘Object’ type is assignable to very few other types. Did you mean to use the ‘any’ type instead?
Type ‘Object’ is missing the following properties from type ‘string[]’: length, pop, push, concat, and 26 more.
24 return this.http.get(‘/getallfiles’);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~