Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

File Upload Using Angular4/Microservice

DZone's Guide to

File Upload Using Angular4/Microservice

Learn how to create an upload functionality to invoke a microservice in an Angular 4 app using the Angular.io plugin in Eclipse.

· Microservices Zone ·
Free Resource

Containerized Microservices require new monitoring. See why a new APM approach is needed to even see containerized applications.

Uploading a file is a regular feature of web programming. Every business needs this ability, and we know how to upload a file using JSP/HTML as a front-end and servlet/struts/Spring MVC as the server end. But how can you achieve it with an Angular 4/microservice combination?

In this tutorial, I will show you how step-by-step, but before that, let me clarify one thing: I am assuming you have a basic understanding of Angular 4 and microservices.

Now let's jump directly on the problem statement. I want to create an upload functionality which invokes a FileUpload microservice and store the profile picture of an Employee.

Let's create an Angular 4 project using Angular.io plugin in Eclipse. After creating the application, modify the app.component.ts file under the app module.

import { UploadFileService } from './fileUpload.service';
import { Component } from '@angular/core';
import { HttpClient, HttpResponse, HttpEventType } from '@angular/common/http';


@Component({
  selector: 'app-root',
  templateUrl: './view.component.html',
  styleUrls: ['./app.component.css'],
  providers: [UploadFileService]
})

export class AppComponent {
selectedFiles: FileList;
   currentFileUpload: File;
    constructor(private uploadService: UploadFileService) {}
  selectFile(event) {
    this.selectedFiles = event.target.files;
  }
  upload() {

    this.currentFileUpload = this.selectedFiles.item(0);
    this.uploadService.pushFileToStorage(this.currentFileUpload).subscribe(event => {
     if (event instanceof HttpResponse) {
        console.log('File is completely uploaded!');
      }
    });

    this.selectedFiles = undefined;
  }

}

In the @Component decorator, I changed the template URL to view.component.html, which actually holds the FileUpload form components. After that, I add an UploadService as a Provider, which actually posts the selected files to the microservice.

Now, I define a method called selectFile, which captures an event (OnChange event in the fileUpload form field) and extracts the file from the target form fields, in this case, the File form fields.

Then I add another method called upload , which calls the file upload service and subscribes itself to Observable<HttpResponse>.

Here is the view.component.html file:

<div style="text-align:center">
	<label>
		<input type="file" (change)="selectFile($event)">
		</label>
		<button [disabled]="!selectedFiles"
(click)="upload()">Upload</button>
	</div>

Here, I just added a file upload field, and when we select a file, an onchange event will be fired, which calls the selectFile method and passes that event to it.

Next, I call the upload method.

Let's see the file upload 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<{}>> {
    const formdata: FormData = new FormData();
    formdata.append('file', file);

    const req = new HttpRequest('POST', 'http://localhost:8085/profile/uploadpicture', formdata, {
      reportProgress: true,
      responseType: 'text'
    }

    );

    return this.http.request(req);
  }

}

Here I created a Formdata Object and added the uploaded File into it. Using Angular HTTP, I post the form data to a microservice running on port 8085 and publish a REST endpoint called /profile/uploadpicture.

Hooray, we successfully wrote the UI part for File upload using Angular4!

If you start the Angular(ng serve), it will look like the following:

Image title

Let's build the microservice part. Create a project called EmployeeFileUpload service in STS or using start.spring.io. Select a Eureka client module to register this microservice with Eureka.

After that, rename the application.properties to the bootstrap property. Add the following entry:

spring.application.name=EmployeePhotoStorageBoard
eureka.client.serviceUrl.defaultZone:http://localhost:9091/eureka
server.port=8085
security.basic.enable: false   
management.security.enabled: false 

My Eureka server is located on port 9091. I give a logical name to this microservice, calling it EmployeePhotoStorageService, which runs on port 8085 .

Now I am going to create a REST Controller, which accepts the request from Angular and binds the Multipart Form.

Let see the code snippets of FileUploadController:

package com.example.EmployeePhotoStorageService.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
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;


@RestController

public class FileController {

 @Autowired
 FileService fileservice;

 @CrossOrigin(origins = "http://localhost:4200") // Call  from Local Angualar
 @PostMapping("/profile/uploadpicture")
 public ResponseEntity < String > handleFileUpload(@RequestParam("file") MultipartFile file) {
  String message = "";
  try {
   fileservice.store(file);
   message = "You successfully uploaded " + file.getOriginalFilename() + "!";
   return ResponseEntity.status(HttpStatus.OK).body(message);
  } catch (Exception e) {
   message = "Fail to upload Profile Picture" + file.getOriginalFilename() + "!";
   return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
  }
 }


}

A few things to notice here: I use a @CrossOrigin annotation, and with this, I instruct Spring to allow the request coming from localhost:4200. In production, the microservice and Angular app are hosted in different domains; to allow the other domain's request, we must provide the cross-origin annotation. I autowired a FileUpload service which actually writes the File content into the disk.

Let's see the FileService code:

package com.example.EmployeePhotoStorageService.controller;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;


@Service
public class FileService {
 private final Path rootLocation = Paths.get("ProfilePictureStore");

 public void store(MultipartFile file) {
  try {
   System.out.println(file.getOriginalFilename());
   System.out.println(rootLocation.toUri());
   Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
  } catch (Exception e) {
   throw new RuntimeException("FAIL!");
  }
 }

}

Here, I create a directory called ProfilePictureStore under the project; it is the same level to src folder. Now I copy the file input stream to the location using java.nio's  Files.copy() static method.

Now, to run this microservice, I have to write the Spring application boot file. Let's see the code:

package com.example.EmployeePhotoStorageService;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@EnableDiscoveryClient
@SpringBootApplication
public class EmployeePhotoStorageService {

 public static void main(String[] args) {
  SpringApplication.run(EmployeePhotoStorageService.class, args);
 }


}

Ok, we are all set. Only the last piece is missing from this tutorial - the pom.xml file.

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>EmployeeFileUploadService</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>EmployeeDashBoardService</name>
	<description>Demo project for Spring Boot</description>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.4.RELEASE</version>
		<relativePath/>
		<!-- lookup parent from repository -->
	</parent>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Dalston.SR1</spring-cloud.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-jersey</artifactId>
		</dependency>
		<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>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-feign</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-ribbon</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-hystrix</artifactId>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>


That's it. If we run the microservice and upload a file from Angular, we can see that file is stored in ProfilePictureStore folder. Very easy, isn't it?

Conclusion

This is a very simple example, or I can say, a prototype of file upload, without any validation or passing any information from the UI, apart from the raw file like comments, file name, tags, etc. You can enrich this basic example using the Formdata Object in Angular. Another observation: I directly call the microservice instance from Angular. That is not the case in production, where you have to introduce a Zuul API gateway which accepts the request from Angular, does some security checking, then communicates with Eureka and routes the request to the actual microservice for simplicity - I just skipped that part.

Automatically manage containers and microservices with better control and performance using Instana APM. Try it for yourself today.

Topics:
angular 4 ,angular ,microservices ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}