Angular 6 — File Upload

File upload in Angular can be quite hard. You need to deal with files in JavaScript and Backend API. In this article, we will learn how to do both.

First, we will create a PHP file to accept file uploads. Your PHP file code like the following

<?php
$name = $_POST['name'];
if($_FILES['file']){
$path = 'uploads/';
if (!file_exists($path)) {
mkdir($path, 0777, true);
}
$originalName = $_FILES['file']['name'];
$ext = '.'.pathinfo($originalName, PATHINFO_EXTENSION);
$t=time();
$generatedName = md5($t.$originalName).$ext;
$filePath = $path.$generatedName;
if (move_uploaded_file($_FILES['file']['tmp_name'], $filePath)) {
echo json_encode(array(
'result' => 'success',
'status' => true,
));
}
}
?>

Here, we getting two inputs using the POST method.

$name = $_POST['name']; //first Input
$_FILES['file'] //file input - second

The following code checks the upload directory already exist. Otherwise, It will create a new directory.

$path = 'uploads/';
if (!file_exists($path)) {
mkdir($path, 0777, true);
}

Sometimes the different files have the same name. During file upload, the old file replaced by the new file. The following code reduce this

$originalName = $_FILES['file']['name'];
$ext = '.'.pathinfo($originalName, PATHINFO_EXTENSION);
$t=time();
$generatedName = md5($t.$originalName).$ext;
$filePath = $path.$generatedName;

The uploaded files are stored in a directory somewhere on your server.

move_uploaded_file($_FILES['file']['tmp_name'], $filePath

this code does that work.

Next, we will move on to Angular Part. We need to create a new angular project. We are going to use the angular-cli for this project. Create a new application by opening a command prompt at the desired location and type:

ng new fileUpload

Because we will need some complex UI-elements such as Form Elements, Progress Bar. I decided to use the Bootstrap for this project. Install the following dependencies using npm.

npm install bootstrap --save
npm install jquery
--save
npm install dropify
--save

Here I am using dropify. It's a JQuery plugin for styling the file input field. So I need to install JQuery and dropify.

To make the css and js files available in our app, we need to import it into our angular.json file:

"styles": [
"node_modules/bootstrap/dist/css/bootstrap.css",
"node_modules/bootstrap/dist/css/bootstrap-reboot.css",
"node_modules/bootstrap/dist/css/bootstrap-grid.css",
"src/styles.scss",
"node_modules/dropify/dist/css/dropify.css"
],
"scripts": [
"node_modules/jquery/dist/jquery.js",
"node_modules/bootstrap/dist/js/bootstrap.js",
"node_modules/dropify/dist/js/dropify.js"
]

Crete a new service using angular-cli generators.

ng g service upload

Import ReactiveFormModule and HttpClientModule into your app module.

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {ReactiveFormsModule} from "@angular/forms";
import {HttpClientModule} from "@angular/common/http";

@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ReactiveFormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Inside of upload service, we need to use the HttpClient, so we request it using dependency injection.
Also, the service will contain only one method called “uploadFile”.

upload.service.ts

import { Injectable } from '@angular/core';
import {HttpClient} from "@angular/common/http";

@Injectable({
providedIn: 'root'
})
export class UploadService {

constructor(private http: HttpClient) { }

uploadFile(formData){
const url = 'http://localhost/filetest/file.php';
return this.http.post(url, formData, {
reportProgress: true,
observe: 'events'
});
}
}

Url used to points the backend API URL

const url = 'http://localhost/filetest/file.php';

the following code is used for getting the file upload status in percentage

{
reportProgress: true,
observe: 'events'
}

Inside of app component class, we need to use the FormBuilder, UploadService, DomSanitizer, so we request it using dependency injection.

constructor(private fb: FormBuilder, private upService: UploadService, private _sanitizer: DomSanitizer)

create a new formGruop property for initializing the form.

FileForm: FormGroup;

next, configure the form control elements

this.FileForm = this.fb.group({
'FileName': ['', Validators.compose([Validators.required, Validators.minLength(4), Validators.maxLength(1500)])],
'File': [''],
});

Create a template reference variable to access the file input field inside of your component class

@ViewChild('File') InputFile;

Create a file property with File annotation for holding file information.

UploadFile: File;

Declare $ as a global variable of the component class for accessing the jquery functionalities

declare var $: any;

On ngOnInit() initialize the dropify plugin.

$(() => {
$('.dropify').dropify();
});

Create a new method inside of component class, called “upload”. This method holding the code for file capture and upload to the server.

upload(value){
const file = this.InputFile.nativeElement;
if (file.files && file.files[0]) {
this.UploadFile = file.files[0];
}
const formData = new FormData();
formData.append('name', data.name);
formData.append('file', this.UploadFile);
this.upService.uploadFile(formData).subscribe(
event => {
if(event.type === HttpEventType.UploadProgress){
this.UploadProgress = Math.round((event.loaded / event.total) * 100);
console.log(this.UploadProgress);
} else if(event.type === HttpEventType.Response) {
let response: any;
response = event.body;
console.log(response);
}
}
);

}

the following code used for getting the file from the input field.

const file = this.InputFile.nativeElement;
if (file.files && file.files[0]) {
this.UploadFile = file.files[0];
}

Here, I call the uploadFile method from upload services.

this.upService.uploadFile(formData).subscribe(
event => {
if(event.type === HttpEventType.UploadProgress){
this.UploadProgress = Math.round((event.loaded / event.total) * 100);
console.log(this.UploadProgress);
} else if(event.type === HttpEventType.Response) {
let response: any;
response = event.body;
console.log(response);
}
}
);

A lot of Http Events available in angular, in this code I am using UploadProgress and Response events. The UploadProgress event for getting file upload percentage and the Response event for the final result.

Inside of app component template, we need to create a form and a progress bar.

app.component.html

<div class="container">
<form [formGroup]="FileForm" (ngSubmit)="upload(FileForm.value)">
<div class="form-group">
<label for="email">File name</label>
<input type="text" class="form-control" id="email" formControlName="FileName">
</div>
<div class="form-group">
<label>File</label>
<input type="file" class="form-control" class="dropify" formControlName="File" #File>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<br>
<div class="progress">
<div class="progress-bar progress-bar-success bg-success progress-bar-striped" role="progressbar" attr.aria-valuenow="{{UploadProgress}}" aria-valuemin="0" aria-valuemax="100" [style]="getUploadProgress(UploadProgress)">
{{UploadProgress}}% Complete (success)
</div>
</div>
</div>

Reactive form approach used here.

<form [formGroup]="FileForm" (ngSubmit)="upload(FileForm.value)">

A FormGroup aggregates the values of each FormControl child into one object, with each control name as the key.

<input type="text" class="form-control" id="email" formControlName="FileName">

this input field gets the name from the file. formControlName="FileName" Syncs a FormControl in an existing FormGroup to a form control element by name.

<input type="file" class="form-control" class="dropify" formControlName="File" #File>

this is the file input field. Add a template reference #File variable for file input field. we can get the file from using this template reference variable in the component class.

<div class="progress">
<div class="progress-bar progress-bar-success bg-success progress-bar-striped" role="progressbar" attr.aria-valuenow="{{UploadProgress}}" aria-valuemin="0" aria-valuemax="100" [style]="getUploadProgress(UploadProgress)">
{{UploadProgress}}% Complete (success)
</div>
</div>

this is the code for the bootstrap progress bar.