Angular 11 + SpringBoot Upload/Download MultipartFile

Angular 11 SpringBoot Upload Download Files

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.

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

angular-6-upload-multipart-files-spring-boot-server-project-structure-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

angular-6-upload-multipart-files-spring-boot-server-project-structure-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.

angular-6-upload-multipart-file-spring-boot-angular-overview

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:

angular-6-upload-multipart-files-spring-boot-server-browser-result

Inside Spring Project folder, open upload-dir folder:

angular-6-upload-multipart-files-spring-boot-server-folder-result

IV. Sourcecode

SpringBootFileUpload-Server
Angular6Upload-Client

2 1 vote
Article Rating
Subscribe
Notify of
guest
3.5K Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments