Angular 12 + Spring Boot File Upload Example(2024) | CodeUsingJava






















Angular 12 + Spring Boot File Upload Example(2024)

File Uploading and retrieval of uploaded files are the common tasks that is performed in an enterprise applications. We will create such operations using Angular 11 and Spring Boot.
In this example, user can upload files, view all the uploaded files and download the specific file by clicking on that file.
In case the size of uploaded file is larger than expected, then exception is thrown.
The flow can be explained as follows-
Flow Digram

Spring Boot Project Structure:-

This will be the standard directory layout for maven project structure-
Spring Boot Maven Project Structure
We need to start by creating a Maven pom.xml(Project Object Model) file. The pom.xml file contains the project configuration details.
	<?xml version="1.0" encoding="UTF-8"?>
	<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
		<modelVersion>4.0.0</modelVersion>
		<parent>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-parent</artifactId>
			<version>2.2.4.RELEASE</version>
			<relativePath/> <!-- lookup parent from repository -->
		</parent>
		<groupId>com.codeusingjava</groupId>
		<artifactId>springFileUploading</artifactId>
		<version>0.0.1-SNAPSHOT</version>
		<name>springFileUploading</name>
		<description>Spring Boot upload multipart files with Angular</description>
	
		<properties>
			<java.version>1.8</java.version>
		</properties>
	
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-web</artifactId>
			</dependency>
	
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-starter-test</artifactId>
				<scope>test</scope>
				<exclusions>
					<exclusion>
						<groupId>org.junit.vintage</groupId>
						<artifactId>junit-vintage-engine</artifactId>
					</exclusion>
				</exclusions>
			</dependency>
		</dependencies>
	
		<build>
			<plugins>
				<plugin>
					<groupId>org.springframework.boot</groupId>
					<artifactId>spring-boot-maven-plugin</artifactId>
				</plugin>
			</plugins>
		</build>
	
	</project>
	

Inside the application.properties file, we define the max upload size and request size for the file.
spring.servlet.multipart.max-file-size=2MB
spring.servlet.multipart.max-request-size=2MB

The model class for the file can be defined as follows-
	package com.codeusingjava.model;

	public class FileInfo {
	  private String name;
	  private String url;
	
	  public FileInfo(String name, String url) {
		this.name = name;
		this.url = url;
	  }
	
	  public String getName() {
		return this.name;
	  }
	
	  public void setName(String name) {
		this.name = name;
	  }
	
	  public String getUrl() {
		return this.url;
	  }
	
	  public void setUrl(String url) {
		this.url = url;
	  }
	}	

After uploading the file to the end mapping, a response message is generated. The response class has only one parameter and can be defined as follows-
	package com.codeusingjava.upload.message;

public class Response {
  private String message;

  public Response(String message) {
    this.message = message;
  }

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }

}

The service method of the file controller can be defined as follows-
The Resource can be defined as any piece of data that can be accessed without depending on the location of program code.
The Stream is defined as the collection of elements. Here we have defined the stream and the extra data i.e Path is provided in the stream. The Path is used for determining the path of the file system.
	package com.codeusingjava.service;

	import java.nio.file.Path;
	import java.util.stream.Stream;
	
	import org.springframework.core.io.Resource;
	import org.springframework.web.multipart.MultipartFile;
	
	public interface FilesStoreService {
	  public void init();
	
	  public void save(MultipartFile file);
	
	  public Resource load(String filename);
	
	  public void deleteAll();
	
	  public Stream<Path> loadAll();
	}
	

The implementation class for the FilesStoreService can be defined as follows-
The init method initializes a folder where the files are to be uploaded.
	package com.codeusingjava.service;

	import java.io.IOException;
	import java.net.MalformedURLException;
	import java.nio.file.Files;
	import java.nio.file.Path;
	import java.nio.file.Paths;
	import java.util.stream.Stream;
	
	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 FilesStoreServiceImpl implements FilesStoreService {
	
	  private final Path root = Paths.get("uploads");
	
	  @Override
	  public void init() {
		try {
		  Files.createDirectory(root);
		} catch (IOException e) {
		  throw new RuntimeException("Could not initialize folder for upload!");
		}
	  }
	
	  @Override
	  public void save(MultipartFile file) {
		try {
		  Files.copy(file.getInputStream(), this.root.resolve(file.getOriginalFilename()));
		} catch (Exception e) {
		  throw new RuntimeException("Could not store the file. Error: " + e.getMessage());
		}
	  }
	
	  @Override
	  public Resource load(String filename) {
		try {
		  Path file = root.resolve(filename);
		  Resource resource = new UrlResource(file.toUri());
	
		  if (resource.exists() || resource.isReadable()) {
			return resource;
		  } else {
			throw new RuntimeException("Could not read the file!");
		  }
		} catch (MalformedURLException e) {
		  throw new RuntimeException("Error: " + e.getMessage());
		}
	  }
	
	  @Override
	  public void deleteAll() {
		FileSystemUtils.deleteRecursively(root.toFile());
	  }
	
	  @Override
	  public Stream<Path> loadAll() {
		try {
		  return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
		} catch (IOException e) {
		  throw new RuntimeException("Could not load the files!");
		}
	  }
	
	}
	

We define a method for handling exception named FileUploadingExceptionAdvice in case the size of uploaded file is more then expected in a separate class known as FileUploadingExceptionAdvice as follows-
The ControllerAdvice is used for writing a global code which we can apply to different controllers.
The ExceptionHandler annotation is used for handling exception.
	package com.codeusingjava.files.upload.exception;

	import org.springframework.web.multipart.MaxUploadSizeExceededException;
	import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
	
	import com.codeusingjava.upload.message.Response;
	
	import org.springframework.http.HttpStatus;
	import org.springframework.http.ResponseEntity;
	import org.springframework.web.bind.annotation.ControllerAdvice;
	import org.springframework.web.bind.annotation.ExceptionHandler;
	
	@ControllerAdvice
	public class FileUploadingExceptionAdvice extends ResponseEntityExceptionHandler {
	
	  @ExceptionHandler(MaxUploadSizeExceededException.class)
	  public ResponseEntity<Response> handleMaxSizeException(MaxUploadSizeExceededException exc) {
		return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new Response("File too large!"));
	  }
	}

The main class for file upload can be defined as follows-
The fileStorageClass is used for storing as well as retrieving the uploaded files.
	package com.codeusingjava;

	import javax.annotation.Resource;
	
	import org.springframework.boot.CommandLineRunner;
	import org.springframework.boot.SpringApplication;
	import org.springframework.boot.autoconfigure.SpringBootApplication;
	
	import com.codeusingjava.service.FilesStoreService;
	
	@SpringBootApplication
	public class SpringBootFileUploadingApplication implements CommandLineRunner {
	  @Resource
	  FilesStoreService storageService;
	
	  public static void main(String[] args) {
		SpringApplication.run(SpringBootFileUploadingApplication.class, args);
	  }
	
	
	  @Override
	  public void run(String... arg) throws Exception {
		storageService.deleteAll();
		storageService.init();
	  }
	}
	
The output for the Spring boot application is as follows-
Spring Boot Maven Project Structure

Angular Project Structure:-

The project structure for the Angular can be defined as follows-
Angular Project Structure
Firstly we import the HttpClientModule inside the app.module.ts.
This file needs to be updated by importing all the components in it.
	import { NgModule } from '@angular/core';
	import { BrowserModule } from '@angular/platform-browser';
	import { HttpClientModule } from '@angular/common/http';
	import { AppComponent } from './app.component';
	import { FileuploadingComponent } from './fileuploading/fileuploading.component';
	
	@NgModule({
	  declarations: [
		AppComponent,
		FileuploadingComponent
	  ],
	  imports: [
		BrowserModule,
		HttpClientModule 
	  ],
	  providers: [],
	  bootstrap: [AppComponent]
	})
	export class AppModule { }
	
We import the bootstrap inside the style.css file-
	/* You can add global styles to this file, and also import other style files */
	@import url(https://unpkg.com/bootstrap@4.1.0/dist/css/bootstrap.min.css)
	
Now we create a service for uploading the files using the following command-
This service contains method by which user can save as well as upload the files. ng generate service services/fileuploading
Angular Service
The fileuploading.service.ts can be written as follows-
With the help of Observable, the procedure can be examined . It is returned by the upload method.
The HttpClient is used for communicating with the spring boot.
	import { HttpClient, HttpEvent, HttpRequest } from '@angular/common/http';
	import { Injectable } from '@angular/core';
	import { Observable } from 'rxjs';
	
	@Injectable({
	  providedIn: 'root'
	})
	export class FileuploadingService {
	
	  private baseUrl = 'http://localhost:8080';
	
	  constructor(private http: HttpClient) { }
	
	  upload(file: File): Observable<HttpEvent<any>> {
		const formData: FormData = new FormData();
	
		formData.append('file', file);
	
		const req = new HttpRequest('POST', `${this.baseUrl}/upload`, formData, {
		  reportProgress: true,
		  responseType: 'json'
		});
	
		return this.http.request(req);
	  }
	
	  getFiles(): Observable<any> {
		return this.http.get(`${this.baseUrl}/files`);
	  }
	}	
We also create a component for uploading the file using the following command- ng generate component uploadfile
Angular Component
The fileupload .component.ts file is as follows-
The selectFile method can be used for accessing the selected files.
		import { Component, OnInit } from '@angular/core';
		import { HttpEventType, HttpResponse } from '@angular/common/http';
		import { Observable } from 'rxjs';
		import { FileuploadingService } from '../services/fileuploading.service';
		@Component({
		  selector: 'app-fileuploading',
		  templateUrl: './fileuploading.component.html',
		  styleUrls: ['./fileuploading.component.css']
		})
		export class FileuploadingComponent implements OnInit {
		
		  selectedFiles?: FileList;
		  currentFile?: File;
		  progress = 0;
		  message = '';
		
		  fileInfos?: Observable<any>;
		
		  constructor(private fileuploadingService: FileuploadingService) { }
		  ngOnInit() {
			this.fileInfos = this.fileuploadingService.getFiles();
		  }
		
		  selectFile(event: any) {
			this.selectedFiles = event.target.files;
		  }
		
		  upload(): void {
			this.progress = 0;
		  
			if (this.selectedFiles) {
			  const file: File | null = this.selectedFiles.item(0);
		  
			  if (file) {
				this.currentFile = file;
		  
				this.fileuploadingService.upload(this.currentFile).subscribe(
				  (event: any) => {
					if (event.type === HttpEventType.UploadProgress) {
					  this.progress = Math.round(100 * event.loaded / event.total);
					} else if (event instanceof HttpResponse) {
					  this.message = event.body.message;
					  this.fileInfos = this.fileuploadingService.getFiles();
					}
				  },
				  (err: any) => {
					console.log(err);
					this.progress = 0;
		  
					if (err.error && err.error.message) {
					  this.message = err.error.message;
					} else {
					  this.message = 'Could not upload the file!';
					}
		  
					this.currentFile = undefined;
				  });
			  }
		  
			  this.selectedFiles = undefined;
			}
		  }
		}
		

The fileuploading.component.html can be written as follows-
The content of this file would be visible to the user.
		<div class="row">
    <div class="col-8">
      <label class="btn btn-default p-0">
        <input type="file" (change)="selectFile($event)" />
      </label>
    </div>
  
    <div class="col-4">
      <button class="btn btn-success btn-sm" [disabled]="!selectedFiles" (click)="upload()">
        Upload
      </button>
    </div>
  </div>
  
  <div *ngIf="currentFile" class="progress my-3">
    <div
      class="progress-bar progress-bar-info progress-bar-striped"
      role="progressbar"
      attr.aria-valuenow="{{ progress }}"
      aria-valuemin="0"
      aria-valuemax="100"
      [ngStyle]="{ width: progress + '%' }"
    >
      {{ progress }}%
    </div>
  </div>
  
  <div *ngIf="message" class="alert alert-secondary" role="alert">{{ message }}</div>
  
  <div class="card mt-3">
    <div class="card-header">List of Files</div>
    <ul
      class="list-group list-group-flush"
      *ngFor="let file of fileInfos | async"
    >
      <li class="list-group-item">
        <a href="{{ file.url }}">{{ file.name }}</a>
      </li>
    </ul>
  </div>
	

We update the app.component.html file by writing the component of fileuploading as follows-
		<div class="container text-center" style="width:600px">
		<div class="my-6">
		  <h4>{{ title }}</h4>
		</div>
		 <br>
		<app-fileuploading></app-fileuploading>
	  </div>
	

Downloads-

Angular 12 Code example
Spring Boot example