In the tutorial, we show how to upload/download files from Angular 6 Client to MySQL with SpringBoot RestAPIs.
Related posts:
– Angular 6 – Upload/Get MultipartFile to/from Spring Boot Server
– How to upload MultipartFile with Spring Boot
Technologies
- Angular 6
- Java 1.8
- Spring Boot 2.0.3.RELEASE
- Maven 3.3.9
- Spring Tool Suite 3.9.0.RELEASE
- MySQL
Overview
SpringBoot Server
We create a SpringBoot project as below:
Using @Lob
annotation and Spring JPA to save file’s data to MySQL.
@Entity @Table(name="file_model") public class FileModel { ... @Lob @Column(name="pic") private byte[] pic; ... @Transactional public interface FileRepository extends JpaRepository<FileModel, Long>{ } |
-> MySQL records:
Angular 6 Client
Project structure ->
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.
Practice
SpringBoot Server
Create SpringBoot project
We create a SpringBoot project with below dependencies:
spring-boot-starter-thymeleaf
spring-boot-starter-web
spring-boot-starter-data-jpa
mysql
-> Details:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> |
Data Model
– Create a View with @JsonView
in ‘View.java’ file:
package com.javasampleapproach.multipartfile.model; public class View { public interface FileInfo {} } |
– Create ‘FileModel.java’ file:
package com.javasampleapproach.multipartfile.model; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.Table; import com.fasterxml.jackson.annotation.JsonView; @Entity @Table(name="file_model") public class FileModel { @Id @GeneratedValue @Column(name = "id") @JsonView(View.FileInfo.class) private Long id; @Column(name = "name") @JsonView(View.FileInfo.class) private String name; @Column(name = "mimetype") private String mimetype; @Lob @Column(name="pic") private byte[] pic; public FileModel(){} public FileModel(String name, String mimetype, byte[] pic){ this.name = name; this.mimetype = mimetype; this.pic = pic; } public Long getId(){ return this.id; } public void setId(Long id){ this.id = id; } public String getName(){ return this.name; } public void setName(String name){ this.name = name; } public String getMimetype(){ return this.mimetype; } public void setMimetype(String mimetype){ this.mimetype = mimetype; } public byte[] getPic(){ return this.pic; } public void setPic(byte[] pic){ this.pic = pic; } } |
JPA Repository
Implement JPA repository in ‘FileRepository.java’ file:
package com.javasampleapproach.multipartfile.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.transaction.annotation.Transactional; import com.javasampleapproach.multipartfile.model.FileModel; @Transactional public interface FileRepository extends JpaRepository<FileModel, Long>{ } |
– Configure @EnableTransactionManagement
in main class ‘SpringBootUploadMultipartFile2MySqlApplication.java’:
package com.javasampleapproach.multipartfile; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringBootUploadMultipartFile2MySqlApplication { public static void main(String[] args) { SpringApplication.run(SpringBootUploadMultipartFile2MySqlApplication.class, args); } } |
Upload/Download RestAPIs
– Implement upload-file RestAPI in ‘UploadFileController.java’ file:
package com.javasampleapproach.multipartfile.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import com.javasampleapproach.multipartfile.model.FileModel; import com.javasampleapproach.multipartfile.repository.FileRepository; @CrossOrigin(origins = "http://localhost:4200") @RestController public class UploadFileController { @Autowired FileRepository fileRepository; /* * MultipartFile Upload */ @PostMapping("/api/file/upload") public String uploadMultipartFile(@RequestParam("file") MultipartFile file) { try { // save file to PostgreSQL FileModel filemode = new FileModel(file.getOriginalFilename(), file.getContentType(), file.getBytes()); fileRepository.save(filemode); return "File uploaded successfully! -> filename = " + file.getOriginalFilename(); } catch ( Exception e) { return "FAIL! Maybe You had uploaded the file before or the file's size > 500KB"; } } } |
– Implement download/retrieve files RestAPIs in ‘DownloadFileController.java’ file:
package com.javasampleapproach.multipartfile.controller; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.annotation.JsonView; import com.javasampleapproach.multipartfile.model.FileModel; import com.javasampleapproach.multipartfile.model.View; import com.javasampleapproach.multipartfile.repository.FileRepository; @CrossOrigin(origins = "http://localhost:4200") @RestController public class DownloadFileController { @Autowired FileRepository fileRepository; /* * List All Files */ @JsonView(View.FileInfo.class) @GetMapping("/api/file/all") public List<FileModel> getListFiles() { return fileRepository.findAll(); } /* * Download Files */ @GetMapping("/api/file/{id}") public ResponseEntity<byte[]> getFile(@PathVariable Long id) { Optional<FileModel> fileOptional = fileRepository.findById(id); if(fileOptional.isPresent()) { FileModel file = fileOptional.get(); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"") .body(file.getPic()); } return ResponseEntity.status(404).body(null); } } |
– Configure cross-origin
for Angular-Client which running at port 4200
->
@CrossOrigin(origins = "http://localhost:4200")
MySQL Connection
– Add setting in ‘application.properties’ file:
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?useSSL=false spring.datasource.username=root spring.datasource.password=12345 spring.jpa.generate-ddl=true spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false spring.jpa.hibernate.ddl-auto=create-drop |
Angular 6 Client
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.
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 { } |
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<HttpEvent<{}>> { const formdata: FormData = new FormData(); formdata.append('file', file); const req = new HttpRequest('POST', 'http://localhost:8080/api/file/upload', formdata, { reportProgress: true, responseType: 'text' }); return this.http.request(req); } getFiles(): Observable<any> { return this.http.get('http://localhost:8080/api/file/all'); } } |
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<string[]>; 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="http://localhost:8080/api/file/{{fileUpload.id}}">{{fileUpload.name}}</a> |
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> |
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> |
Run & Check Results
Run SpringBoot Server (using mvn spring-boot:run
) and Angular 6 Client App (using ng serve
)
Then open Browser with url http://localhost:4200/
.
-> Upload files and show list of Files:
-> MySQL records:
-> Click to the links to download files:
SourceCode
Last updated on February 6, 2020.
Is it to download an xls?
where is the main class in the downloaded source code?
How would i display it?
You are amazing…
But I have one question, what if I just want post data object from formless form? Do I still need to use HttpEvent Observable? Can post post method and subscribe to it, from a component?
Hi I meant from a formly object, … typo error. My apologies.
Thank You. Code is good….