Creating Angular 5 Multiple File Upload And ASP.NET Core 2.1

In this article, we'll learn how to upload multiple files using Angular 5 and ASP.NET Core 2.1. the purpose of article to demonstrate multiple file upload with progress bar without using any angular third party plug-in

Introduction 

In this article, we'll learn how to upload multiple files using Angular 5 and ASP.NET Core 2.1. The purpose of the article is to demonstrate multiple file upload with progress bar without using any Angular third-party plug-in.

Prerequisite :

  1. .NET Core latest version 2.1: download from here.
  2. Angular 5: upgrade steps here.

The source code is available on GitHub here.

In Angular 5, Http Client module has been introduced and Http module deprecated. The Http Client module has introduced a few nice features which help in achieving the goal of this article.

Http Client Module API

  1.   import { HttpClientModule } from '@angular/common/http';

Http Client Features

  1. Progress Events for both Request upload and Response Download
  2. JSON parsing by default , no need to explicitly json parsing.
  3. Immutable Request/Response objects.
  4. Performance Improvements

A glimpse of Angular Http Events. You have to subscribe HTTP client request for events after that you will get all the events notification like HttpEventType enum below

HttpProgressEvent Interface

Progess event type is either upload or download,

  1. /** 
  2.  * Base interface for progress events. 
  3.  * 
  4.  * @stable 
  5.  */  
  6. export interface HttpProgressEvent {  
  7.     /** 
  8.      * Progress event type is either upload or download. 
  9.      */  
  10.     type: HttpEventType.DownloadProgress | HttpEventType.UploadProgress;  
  11.     /** 
  12.      * Number of bytes uploaded or downloaded. 
  13.      */  
  14.     loaded: number;  
  15.     /** 
  16.      * Total number of bytes to upload or download. Depending on the request or 
  17.      * response, this may not be computable and thus may not be present. 
  18.      */  
  19.     total?: number;  
  20. }  

HttpEventType enum

Type enumeration for the different kinds of `HttpEvent`

  1. /** 
  2.  * Type enumeration for the different kinds of `HttpEvent`. 
  3.  * 
  4.  * @stable 
  5.  */  
  6. export declare enum HttpEventType {  
  7.     /** 
  8.      * The request was sent out over the wire. 
  9.      */  
  10.     Sent = 0,  
  11.     /** 
  12.      * An upload progress event was received. 
  13.      */  
  14.     UploadProgress = 1,  
  15.     /** 
  16.      * The response status code and headers were received. 
  17.      */  
  18.     ResponseHeader = 2,  
  19.     /** 
  20.      * A download progress event was received. 
  21.      */  
  22.     DownloadProgress = 3,  
  23.     /** 
  24.      * The full response including the body was received. 
  25.      */  
  26.     Response = 4,  
  27.     /** 
  28.      * A custom event from an interceptor or a backend. 
  29.      */  
  30.     User = 5,  
  31. }  

Let's get started the things in action. Let’s open the visual studio and create the new ASP.NET Core Web Application project. See the below screen

 

 

After that first, you have to select .NET Core version from top of the dropdown list so I have selected 2.1 version then choose the angular template which we want to create. See the below screen

 

 

We have created angular template project successfully. The latest version of visual studio automatically creates the Angular 5 template so need to explicit update or install. Build the project so that it will download all node modules.

Let's open the command prompt and browse the clientApp folder in CMD

Create new Component using Angular cli. You have to start command with ng for all angular CLI commands

To check the versions,

Command

ng --version

 

 

Command 

ng generate component upload

 

 

Upload Component code 

  1. import { Component } from '@angular/core';  
  2. import { HttpClient, HttpRequest, HttpEventType, HttpResponse } from '@angular/common/http'  
  3.   
  4. import { Uploader } from '../entities/uploader';  
  5. import { UploadQueue } from '../entities/uploadqueue';  
  6.   
  7.   
  8. @Component({  
  9.   selector: 'app-upload',  
  10.   templateUrl: './upload.component.html'  
  11. })  
  12.   
  13.   
  14. export class UploadComponent {  
  15.   
  16.   //getter : get overall progress  
  17.   get progress(): number {  
  18.     let psum = 0;  
  19.   
  20.     for (let entry of this.uploader.queue) {  
  21.       psum += entry.progress;  
  22.     }  
  23.   
  24.     if (psum == 0)  
  25.       return 0;  
  26.   
  27.     return Math.round(psum / this.uploader.queue.length);  
  28.   };  
  29.   public message: string;  
  30.   public uploader: Uploader = new Uploader();  
  31.   
  32.   constructor(private http: HttpClient) {  
  33.     this.message = '';      
  34.   }  
  35.   
  36.   onFilesChange(fileList: Array<File>) {  
  37.     for (let file of fileList) {  
  38.       this.uploader.queue.push(new UploadQueue(file));  
  39.     };    
  40.   }  
  41.   
  42.   onFileInvalids(fileList: Array<File>) {  
  43.     //TODO handle invalid files here  
  44.   }  
  45.     
  46.   onSelectChange(event: EventTarget) {  
  47.     let eventObj: MSInputMethodContext = <MSInputMethodContext>event;  
  48.     let target: HTMLInputElement = <HTMLInputElement>eventObj.target;  
  49.     let files: FileList = target.files;  
  50.     let file = files[0];  
  51.     if (file) {  
  52.       this.uploader.queue.push(new UploadQueue(file));  
  53.       //console.log(file);  
  54.       console.log('Total Count:' + this.uploader.queue.length);        
  55.     }  
  56.       
  57.   }  
  58.   
  59.   // upload   
  60.     upload(id) {  
  61.     if (id == null)  
  62.             return;  
  63.   
  64.       let selectedFile = this.uploader.queue.find(s => s.id == id);  
  65.       if (selectedFile) {  
  66.         const formData = new FormData();  
  67.         formData.append(selectedFile.file.name, selectedFile.file);  
  68.   
  69.         const uploadReq = new HttpRequest('POST', `api/upload`, formData, {  
  70.           reportProgress: true,  
  71.         });    
  72.         
  73.         this.http.request(uploadReq).subscribe(event => {  
  74.           if (event.type === HttpEventType.UploadProgress) {             
  75.             selectedFile.progress = Math.round(100 * event.loaded / event.total);  
  76.           }  
  77.           else if (event.type === HttpEventType.Response)  
  78.             selectedFile.message = event.body.toString();  
  79.         });  
  80.       }  
  81.   }  
  82.   //upload all selected files to server  
  83.   uploadAll() {  
  84.     //find the remaning files to upload  
  85.     let remainingFiles = this.uploader.queue.filter(s => !s.isSuccess);  
  86.     for (let item of remainingFiles) {  
  87.       this.upload(item.id);  
  88.     }  
  89.   }  
  90.   
  91.   // cancel all   
  92.   cancelAll() {  
  93.     //TODO  
  94.   }  
  95. }   

Now, I'm going to create some entities/poco classes just to make the separation of things

  1. Uploader - contains the queue of uploadqueue
  2. Uploadqueue - contains the property of uploading queue like a file, progress, issue, success etc.
  3. Guid - to generate random guid string

Command

ng generate class uploadqueue

 

 

UploadQueue Class
  1. import { Guid } from '../entities/guid';  
  2.   
  3. /** 
  4.  * Represents an UploadQueue 
  5.  */  
  6. export class UploadQueue {  
  7.   id: string;  
  8.   file: File;  
  9.   progress: number;  
  10.   message: string;  
  11.   isCancel: boolean;  
  12.   isError: boolean;  
  13.   get isSuccess(): boolean {  
  14.     if (this.progress == 100)  
  15.       return true;  
  16.   
  17.     return false;  
  18.   };  
  19.   
  20.   constructor(file: File) {  
  21.     this.file = file;  
  22.     this.progress = 0;  
  23.     this.id = Guid.newGuid();  
  24.     this.message = '';  
  25.     this.isCancel = false;  
  26.     this.isError = false;  
  27.   }  
  28. }  

Similar other classes like uploader and guid, you will find in entities folder.

Now, I'm going to create an Angular directive for drag and drop support so that you can directly drop the files and also you can allow/restrict the file name extensions.

Commands: ng generate a directive upload.

 

 

Now we have to add the upload link to route root and nav bar so that we can browse the uplaod template and see the things in action

Add root in App module,

  1. { path: 'upload', component: UploadComponent }    

 

We have done with Aangular part, now I'm going to create web API controller for uploading the files

Create the new controller called 'upload' controller 

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.IO;  
  4. using System.Linq;  
  5. using System.Net.Http.Headers;  
  6. using System.Threading.Tasks;  
  7. using Microsoft.AspNetCore.Hosting;  
  8. using Microsoft.AspNetCore.Mvc;  
  9.   
  10. namespace Angular5MultipleFileUpload.Controllers  
  11. {  
  12.   
  13.     [Produces("application/json")]  
  14.     [Route("api/[controller]")]  
  15.     public class UploadController : Controller  
  16.     {          
  17.         private IHostingEnvironment _hostingEnvironment;  
  18.   
  19.         public UploadController(IHostingEnvironment hostingEnvironment)  
  20.         {  
  21.             _hostingEnvironment = hostingEnvironment;  
  22.         }  
  23.   
  24.         [HttpPost]  
  25.         public IActionResult Index()  
  26.         {  
  27.             try  
  28.             {  
  29.                 var file = Request.Form.Files[0];  
  30.                 string folderName = "Upload";  
  31.                 string webRootPath = _hostingEnvironment.WebRootPath;  
  32.                 string newPath = Path.Combine(webRootPath, folderName);  
  33.                 if (!Directory.Exists(newPath))  
  34.                 {  
  35.                     Directory.CreateDirectory(newPath);  
  36.                 }  
  37.                 if (file.Length > 0)  
  38.                 {  
  39.                     string fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');  
  40.                     string fullPath = Path.Combine(newPath, fileName);  
  41.                     using (var stream = new FileStream(fullPath, FileMode.Create))  
  42.                     {  
  43.                         file.CopyTo(stream);  
  44.                     }  
  45.                 }  
  46.                 return Json("Upload Successful.");  
  47.             }  
  48.             catch (Exception ex)  
  49.             {  
  50.                 return Json("Upload Failed: " + ex.Message);  
  51.             }  
  52.         }  
  53.     }  
  54. }  

Demo Screens

 

 

 
 

Summary

In this article, we have learned how to create multiple upload files using angular 5 and asp.net core 2.1

We have learned below Angular CLI commands

  1. Generate : this command is used to create the new components, routes, services, and pipes
  2. Class : to create a new class
  3. Directive : to create the new directive
  4. Component : to create the new component

References

 

  • https://www.microsoft.com/net/download
  • https://update.angular.io/
  • https://angular.io/api/common/http
  • https://angular.io/api/common/http/HttpEventType
  • https://github.com/nemi-chand/Angular5MultipleFileUpload