Angular 11 + SpringBoot Upload/Download MultipartFile
Tags: angular11, springboot, uploadfile, downloadfile
Published: 2021-03-12 15:31:38

https://grokonez.com/angular-11-springboot-upload-download-multipartfile

When uploading files to Servlet containers, application needs to register a <strong>MultipartConfigElement</strong> class. But <strong>Spring Boot</strong> 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:
- <a href="https://grokonez.com/spring-framework/spring-boot/upload-multipartfile-spring-boot">How to upload MultipartFile with Spring Boot</a>
- <a href="https://grokonez.com/spring-framework/spring-boot/multipartfile-create-spring-ajax-multipartfile-application-downloadupload-files-springboot-jquery-ajax-bootstrap">MultipartFile - SpringBoot + JQuery Ajax + Bootstrap.</a>
- <a href="https://grokonez.com/spring-framework/spring-boot/multipartfile-create-spring-angularjs-multipartfile-application-downloadupload-files-springboot-angularjs-bootstrap">MultipartFile – SpringBoot + AngularJs + Bootstrap.</a>

<!--more-->
<h2>I. Technologies</h2>
- Angular 11
- Java 1.8
- Spring Boot 2.0.3.RELEASE
- Maven 3.3.9
- Spring Tool Suite 3.9.0.RELEASE
<h2>II. Overview</h2>
<h3>1. Spring Boot Server</h3>
<img src="https://grokonez.com/wp-content/uploads/2018/06/angular-6-upload-multipart-files-spring-boot-server-project-structure-server.png" alt="angular-6-upload-multipart-files-spring-boot-server-project-structure-server" width="320" height="327" class="alignnone size-full wp-image-13335" />

- <code>StorageService</code> helps to init, delete all files, store file, load file
- <code>UploadController</code> uses <code>StorageService</code> to provide Rest API: POST a file, GET all files
- <strong>application.properties</strong> to configure parameters such as MultipartFile max size...
- Spring Boot Starter Web dependency in <strong>pom.xml</strong>
<h3>2. Angular 11 App Client</h3>
<img src="https://grokonez.com/wp-content/uploads/2018/06/angular-6-upload-multipart-files-spring-boot-server-project-structure-client.png" alt="angular-6-upload-multipart-files-spring-boot-server-project-structure-client" width="263" height="421" class="alignnone size-full wp-image-13336" />

- <code>upload-file.service</code> provides methods: push File to Storage and get Files.
- <code>list-upload.component</code> gets and displays list of Files.
- <code>form-upload.component</code> helps upload File.
- <code>details-upload.component</code> is detail for each item in list of Files.

<img src="https://grokonez.com/wp-content/uploads/2018/06/angular-6-upload-multipart-file-spring-boot-angular-overview.png" alt="angular-6-upload-multipart-file-spring-boot-angular-overview" width="440" height="407" class="alignnone size-full wp-image-13337" />

<h2>III. Practice</h2>
<h3>1. Spring Boot Server</h3>
<h4>1.1 Create Spring Boot project</h4>
With Dependency:
<pre><code class="language-html">&lt;dependency&gt;
	&lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
	&lt;artifactId&gt;spring-boot-starter-web&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre>
<h4>1.2 Create Storage Service for File Systems</h4>
Create <strong>StorageService</strong> 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 <strong>upload directory</strong>

<em>storage/StorageService.java</em>
<pre><code class="language-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!");
		}
	}
}
</code></pre>
<h4>1.3 Create Upload Controller</h4>
<em>controller/UploadController.java</em>
<pre><code class="language-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<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);
	}
}
</code></pre>
<h4>1.4 Config multipart</h4>
Open <strong>application.properties</strong>:
<pre><code class="language-javascript">
spring.servlet.multipart.max-file-size=500KB
spring.servlet.multipart.max-request-size=500KB
</code></pre>
- <code>spring.servlet.multipart.max-file-size</code>: limit total file size for each request.
- <code>spring.servlet.multipart.max-request-size</code>: limit total request size for a multipart/form-data.
<h4>1.5 Init Storage for File System</h4>
<em>SpringBootUploadFileApplication.java</em>
<pre><code class="language-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();
	}
}
</code></pre>
<h3>2. Angular 11 App Client</h3>
<h4>2.0 Generate Service & Components</h4>
Run commands below:
- <code>ng g s upload/UploadFile</code>
- <code>ng g c upload/FormUpload</code>
- <code>ng g c upload/ListUpload</code>
- <code>ng g c upload/DetailsUpload</code>
On each Component selector, delete <code>app-</code> prefix, then change <strong>tslint.json</strong> <code>rules</code> - <code>"component-selector"</code> to <strong>false</strong>.
<h4>2.1 App Module</h4>
<em>app.module.ts</em>
<pre><code class="language-java">
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 { }
</code></pre>
<h4>2.2 Upload File Service</h4>
<em>upload/upload-file.service.ts</em>
<pre><code class="language-java">
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<HttpEvent<{}>> {
    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<any> {
    return this.http.get('/getallfiles');
  }
}
</code></pre>


https://grokonez.com/angular-11-springboot-upload-download-multipartfile