Multiple File Upload in Angular using 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 the article is to demonstrate multiple file upload with progress bar without using any Angular third-party plug-in.
Prerequisite :
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 Features
- Progress Events for both Request upload and Response Download
- JSON parsing by default , no need to explicitly json parsing.
- Immutable Request/Response objects.
- 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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Base interface for progress events.
*
* @stable
*/
export interface HttpProgressEvent {
/**
* Progress event type is either upload or download.
*/
type: HttpEventType.DownloadProgress | HttpEventType.UploadProgress;
/**
* Number of bytes uploaded or downloaded.
*/
loaded: number;
/**
* Total number of bytes to upload or download. Depending on the request or
* response, this may not be computable and thus may not be present.
*/
total?: number;
}
HttpEventType enum
Type enumeration for the different kinds of HttpEvent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* Type enumeration for the different kinds of `HttpEvent`.
*
* @stable
*/
export declare enum HttpEventType {
/**
* The request was sent out over the wire.
*/
Sent = 0,
/**
* An upload progress event was received.
*/
UploadProgress = 1,
/**
* The response status code and headers were received.
*/
ResponseHeader = 2,
/**
* A download progress event was received.
*/
DownloadProgress = 3,
/**
* The full response including the body was received.
*/
Response = 4,
/**
* A custom event from an interceptor or a backend.
*/
User = 5,
}
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 installed version of angular.
Version Command
1
2
3
4
5
ng --version
or
ng -v
Generate Component Command
1
2
3
4
5
6
7
8
9
ng generate component upload
or
ng g c upload
// g --> generate
// c --> component
Upload Component code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { Component } from '@angular/core';
import { HttpClient, HttpRequest, HttpEventType, HttpResponse } from '@angular/common/http'
import { Uploader } from '../entities/uploader';
import { UploadQueue } from '../entities/uploadqueue';
@Component({
selector: 'app-upload',
templateUrl: './upload.component.html'
})
export class UploadComponent {
//getter : get overall progress
get progress(): number {
let psum = 0;
for (let entry of this.uploader.queue) {
psum += entry.progress;
}
if (psum == 0)
return 0;
return Math.round(psum / this.uploader.queue.length);
};
public message: string;
public uploader: Uploader = new Uploader();
constructor(private http: HttpClient) {
this.message = '';
}
onFilesChange(fileList: Array<File>) {
for (let file of fileList) {
this.uploader.queue.push(new UploadQueue(file));
};
}
onFileInvalids(fileList: Array<File>) {
//TODO handle invalid files here
}
onSelectChange(event: EventTarget) {
let eventObj: MSInputMethodContext = <MSInputMethodContext>event;
let target: HTMLInputElement = <HTMLInputElement>eventObj.target;
let files: FileList = target.files;
let file = files[0];
if (file) {
this.uploader.queue.push(new UploadQueue(file));
//console.log(file);
console.log('Total Count:' + this.uploader.queue.length);
}
}
// upload
upload(id) {
if (id == null)
return;
let selectedFile = this.uploader.queue.find(s => s.id == id);
if (selectedFile) {
const formData = new FormData();
formData.append(selectedFile.file.name, selectedFile.file);
const uploadReq = new HttpRequest('POST', `api/upload`, formData, {
reportProgress: true,
});
this.http.request(uploadReq).subscribe(event => {
if (event.type === HttpEventType.UploadProgress) {
selectedFile.progress = Math.round(100 * event.loaded / event.total);
}
else if (event.type === HttpEventType.Response)
selectedFile.message = event.body.toString();
});
}
}
//upload all selected files to server
uploadAll() {
//find the remaning files to upload
let remainingFiles = this.uploader.queue.filter(s => !s.isSuccess);
for (let item of remainingFiles) {
this.upload(item.id);
}
}
// cancel all
cancelAll() {
//TODO
}
}
Now, I’m going to create some entities/poco classes just to make the separation of things
- Uploader - contains the queue of uploadqueue
- Uploadqueue - contains the property of uploading queue like a file, progress, issue, success etc.
- Guid - to generate random guid string
Generate Class Command
1
ng generate class uploadqueue
UploadQueue Class source code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import { Guid } from '../entities/guid';
/**
* Represents an UploadQueue
*/
export class UploadQueue {
id: string;
file: File;
progress: number;
message: string;
isCancel: boolean;
isError: boolean;
get isSuccess(): boolean {
if (this.progress == 100)
return true;
return false;
};
constructor(file: File) {
this.file = file;
this.progress = 0;
this.id = Guid.newGuid();
this.message = '';
this.isCancel = false;
this.isError = false;
}
}
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.
Generate Directive Command
1
2
3
4
5
ng generate directive --name upload
or
ng g d --name 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 ASPNET Core MVC controller called ‘upload’ controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace Angular5MultipleFileUpload.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
public class UploadController : Controller
{
private IHostingEnvironment _hostingEnvironment;
public UploadController(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
[HttpPost]
public IActionResult Index()
{
try
{
var file = Request.Form.Files[0];
string folderName = "Upload";
string webRootPath = _hostingEnvironment.WebRootPath;
string newPath = Path.Combine(webRootPath, folderName);
if (!Directory.Exists(newPath))
{
Directory.CreateDirectory(newPath);
}
if (file.Length > 0)
{
string fileName = ContentDispositionHeaderValue.Parse(file.ContentDisposition).FileName.Trim('"');
string fullPath = Path.Combine(newPath, fileName);
using (var stream = new FileStream(fullPath, FileMode.Create))
{
file.CopyTo(stream);
}
}
return Json("Upload Successful.");
}
catch (Exception ex)
{
return Json("Upload Failed: " + ex.Message);
}
}
}
}
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
- Generate : this command is used to create the new components, routes, services, and pipes
- Class : to create a new class
- Directive : to create the new directive
- Component : to create the new component
References