In this article, you will learn by creating a complete application for Angular 4 Multiple File Upload using ASP.NET Web API and C#
I will describe how to upload multiple files using Angular 4, ASP.NET Web API and C# by extending the solution developed in the previous article – Angular 4 QR Code Generator using ASP.NET Web API and C#. I will add new component for uploading files to the previous article’s source code.
Technologies Used : Angular 4, Web API, C#, HTML 5, Bootstrap
Tools needed : Visual Studio 2017.
Source Code : Complete source code can be found on GitHub repository :
https://github.com/sudipta-chaudhari/angular4_FileUpload_AspNetWebAPI
Using the source code from my previous article Generate QR Code using Angular 4 and ASP.NET Web API, proceed as mentioned in the below steps.
Step (1) : Create Web API controller
Add a new Web API controller and name it UploadController
. Create a POST method which will upload files and then return a string which will have the information on how many files were uploaded successfully and their respective file names.
using System; using System.Collections.Generic; using System.Net.Http; using System.Web; using System.Web.Http; namespace Angular4WebApi.Controllers { public class UploadController : ApiController { [HttpPost] public IHttpActionResult UploadFiles() { int i = 0; int cntSuccess = 0; var uploadedFileNames = new List<string>(); string result = string.Empty; HttpResponseMessage response = new HttpResponseMessage(); var httpRequest = HttpContext.Current.Request; if (httpRequest.Files.Count > 0) { foreach (string file in httpRequest.Files) { var postedFile = httpRequest.Files[i]; var filePath = HttpContext.Current.Server.MapPath("~/UploadedFiles/" + postedFile.FileName); try { postedFile.SaveAs(filePath); uploadedFileNames.Add(httpRequest.Files[i].FileName); cntSuccess++; } catch (Exception ex) { throw ex; } i++; } } result = cntSuccess.ToString() + " files uploaded succesfully.<br/>"; result += "<ul>"; foreach (var f in uploadedFileNames) { result += "<li>" + f + "</li>"; } result += "</ul>"; return Json(result); } } }
Non .NET/Microsoft audience can create any REST api using Node.js, PHP etc to upload files.
The above Web API’s POST method UploadFiles()
gets the files from the HTTP Request object and used a for-each loop to save the files to ‘Upload’ directory on the web server’s root directory’s ‘UploadedFiles’ folder.
Step (2) : Create View for Upload Component
Under app
folder at the root of the application, create a new folder upload
and add two files – upload.component.html
and upload.component.ts
.
In the upload.component.html
file, add a HTML 5 file upload element and set attribute ‘multiple’ to upload multiple files at once. Also, set attribute accept=".pdf"
to accept PDF files only to upload. accept
attribute doesn’t work on IE 11, Edge etc browsers, hence a separate check in Typescript and server side code of Web API is also needed.
Also, in this html file, I have used a custom CSS class spinner
to display a loading spinner which will be controlled by TypeScript for the Upload component to show the spinner while the file upload is in progress and hide the spinner when the file upload completes.
Complete code for upload.component.html
is shown below
<style type="text/css"> #spinner { background-color: rgba(49, 37, 37, 0.2); border-radius: 6px; top: 0; left: 0; height: 100%; width: 100%; position: fixed; content: " "; text-align: center; z-index: 9999; /*Center Div*/ /* Safari, Opera, and Chrome */ display: -webkit-box; -webkit-box-pack: center; -webkit-box-align: center; /* Firefox */ display: -moz-box; -moz-box-pack: center; -moz-box-align: center; /* Internet Explorer 10 */ display: -ms-flexbox; -ms-flex-pack: center; -ms-flex-align: center; } </style> <h2>Upload Files</h2> <div *ngIf="isLoadingData" style="text-align:center" id="spinner"> <img src="./Content/page-loader.gif" /> </div> <input #fileUpload type="file" (change)="fileChangeEvent($event)" placeholder="Upload file..." multiple accept=".pdf" /><br /> <button type="button" (click)="upload()">Upload</button> <button type="button" (click)="cancelUpload()">Cancel</button> <div *ngIf="filesToUpload?.length > 0"><br /><p>{{filesToUpload?.length}} files selected : </p></div> <ul> <li *ngFor="let fileName of selectedFileNames; let i = index"> {{fileName}} </li> </ul> <div [innerHtml]="uploadResult"></div> <div style="color:red"> {{errorMessage}} </div>
The HTML 5 file upload control’s ‘change’ event has function ‘fileChangeEvent’ in which the event of the file upload control is passed. An Upload button is defined and on click of this button, the ‘upload’ function is triggered from TypeScript code. The ‘Cancel Upload’ button clears the upload field and resets any variables in TypeScript code. I will be omitting the code for the ‘Cancel Upload’ button and leave it to the reader.
Step (3) : Create TypeScript for the Upload Component
TypeScript code for the Upload component upload.component.ts
is shown below.
import { Component, ViewChild } from '@angular/core'; import { Http, RequestOptions, Headers, Response } from '@angular/http'; import { Observable } from 'rxjs/Rx'; import { Router } from '@angular/router'; import { UploadedFile } from '../model/uploadedfile'; @Component({ templateUrl: './app/upload/upload.component.html' }) export class UploadComponent { errorMessage: string; filesToUpload: Array<File>; selectedFileNames: string[] = []; public isLoadingData: Boolean = false; @ViewChild('fileUpload') fileUploadVar: any; uploadResult: any; res: Array<string>; constructor(private http: Http, private router: Router) { this.errorMessage = ""; this.filesToUpload = []; this.selectedFileNames = []; this.uploadResult = ""; } fileChangeEvent(fileInput: any) { //Clear Uploaded Files result message this.uploadResult = ""; this.filesToUpload = <Array<File>>fileInput.target.files; for (let i = 0; i < this.filesToUpload.length; i++) { this.selectedFileNames.push(this.filesToUpload[i].name); } } cancelUpload() { this.filesToUpload = []; this.fileUploadVar.nativeElement.value = ""; this.selectedFileNames = []; this.uploadResult = ""; this.errorMessage = ""; } upload() { if (this.filesToUpload.length == 0) { alert('Please select at least 1 PDF files to upload!'); } else if (this.filesToUpload.length > 3) { alert('Please select a maximum of 3 PDF files to upload!'); } else { if (this.validatePDFSelectedOnly(this.selectedFileNames)) { this.uploadFiles(); } } } validatePDFSelectedOnly(filesSelected: string[]) { for (var i = 0; i < filesSelected.length; i++) { if (filesSelected[i].slice(-3).toUpperCase() != "PDF") { alert('Please selecte PDF files only!'); return false; } else { return true; } } } uploadFiles() { this.uploadResult = ""; if (this.filesToUpload.length > 0) { this.isLoadingData = true; let formData: FormData = new FormData(); for (var i = 0; i < this.filesToUpload.length; i++) { formData.append('uploadedFiles', this.filesToUpload[i], this.filesToUpload[i].name); } let apiUrl = "/api/Upload/UploadFiles"; this.http.post(apiUrl, formData) .map((res: Response) => res.json()) .subscribe ( data => { this.uploadResult = data; this.errorMessage = ""; }, err => { console.error(err); this.errorMessage = err; this.isLoadingData = false; }, () => { this.isLoadingData = false, this.fileUploadVar.nativeElement.value = ""; this.selectedFileNames = []; this.filesToUpload = []; } ); } } }
filesToUpload
is an Array of Files and fileUploadVar
is of type ViewChild
which is used to access the HTML 5 fileUpload control from the HTML view in TypeScript code.
upload()
function checks if no file is selected, alerts the user to do the same. Then the function checks if more than 3 files are selected for upload, alerts the user to select a maximum of 3 files. If the above two checks are valid, next the upload function check if all the files selected for upload are PDF files using the validatePDFSelectedOnly(filesSelected: string[])
function, then proceeds to call the uploadFiles()
function to start uploading the files, else alerts the user that some of the files selected are not PDF files. This check is needed for browser like IE11, Edge etc. which don’t support the HTML 5 file upload attributes like ‘accept’.
isLoadingData
variable is used to display a loading spinner by setting it to true when the upload function starts executing and is set to false in the Observable after the uploading files is completed.
Angular HTTP module is used to make a HTTP POST call the to Web API ‘/api/Upload/UploadFiles’ which is a POST api.
Also note the formData
variable which is defined as a type of FormData
. This variable holds all the uploaded files and this is used to pass the data to the POST Web API as formData
.
Step (4) : Add Navigation link for Upload View
In the app.router.ts
file, define create a route path for the upload component. Complete code for the file is shown below.
import { ModuleWithProviders } from '@angular/core'; import { Routes, RouterModule} from '@angular/router'; import { HomeComponent } from './home/home.component'; import { QRCodeComponent } from './qrcode/qrcode.component'; import { UploadComponent } from './upload/upload.component'; export const router: Routes = [ { path: '', redirectTo: 'home', pathMatch: 'full' }, { path: 'home', component: HomeComponent }, { path: 'qrcode', component: QRCodeComponent }, { path: 'upload', component: UploadComponent } ]; export const routes: ModuleWithProviders = RouterModule.forRoot(router);
Step (5) : Run the application
On running the application and navigating to the Upload route, the screen looks as shown in screenshot below.
On selecting some PDF files for upload, the screen looks as in the screenshot below.
On clicking the upload button and while upload is in progress, the busy indicator is displayed as shown in screenshot below.
After the files have been uploaded successfully, the screen looks like as shown in screenshot below.
Navigating to the “UploadedFiles” directotry, you can see the actual files uploaded as shown below.
Hope you followed the article. If you have any comments, questions or suggestions, leave a message and I will try to respond at my earliest.
Thank so much!
LikeLike