Sometimes y'all need to upload files to your server from an Angular awarding to a Web API method. There are many ways to upload a file. In this commodity, I'g going to nowadays a method that works well for pocket-sized files, upwardly to about 1 or two megabytes in size. You lot're going to build two projects; a .Cyberspace Core Web API project and an Angular project. You'll build these ii projects from scratch using the Athwart CLI, .Cyberspace Core, and the Visual Studio Code editor.

The result from this article is a page (as shown in Figure 1) that allows you to select one or more modest files using an <input type="file"> element. You so build a custom FileToUpload object with attributes near the file, plus the file contents. Finally, this FileToUpload object is sent via a Web API call to a method on the server. One time the server has this file, you may choose to salvage it as a file on the server, into a database table, or any other location.

Figure 1: A simple file upload page
Effigy ane: A uncomplicated file upload page

Build a .NET Core Web API

Let's start by edifice the .NET Cadre Web API application to which you lot upload files. Open an instance of Visual Studio Code. From the menu, select View > Final to display a final window at the bottom of the editor. You should run across something that looks like this:

          Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved.  20 C:\Users\YOUR_LOGIN>                  

I'm sure y'all accept a directory somewhere on your hard bulldoze in which y'all create your projects. Navigate to that folder from within the terminal window. For instance, I'k going to go to my D bulldoze and so to the \Samples folder on that drive. Enter the post-obit commands to create a .NET Core Spider web API projection.

          d: cd Samples mkdir FileUploadSample cd FileUploadSample mkdir FileUploadWebApi cd FileUploadWebApi dotnet new webapi                  

Open the Web API Folder

Now that you've built the Web API project, yous demand to add information technology to Visual Studio Lawmaking. Select File > Open Binder... and open the folder where you but created the FileUploadWebApi projection (Figure ii). In my case, this is from the folder D:\Samples\FileUploadSample\FileUploadWebApi. One time you've navigated to this folder, click the Select Folder button.

Figure 2: Add the Web API folder to VS Code.
Figure 2: Add the Web API folder to VS Code.

Load Required Assets

Afterwards loading the binder, VS Lawmaking may download and install some packages. Afterwards a few more than seconds, you should see a new prompt appear (Figure 3) in VS Lawmaking saying that some required avails are missing. Click on the Yes button to add these assets to this projection.

Figure 3: Answer Yes to the prompt to add missing assets.
Figure 3: Answer Yes to the prompt to add together missing assets.

Enable Cors

The Web API projection is going to run on the address localhost:5000 by default. However, when you create a new Angular application, it will run on localhost:4200 by default. This means that each Web application is running on a separate domain from each other. For your Athwart application to telephone call the Web API methods, you lot must tell the Web API that you're allowing Cross-Origin Resource Sharing (CORS). To employ CORS, add the Microsoft.AspNetCore.Cors package to your project. Become back to the terminal window and type the following control:

          dotnet add package Microsoft.AspNetCore.Cors                  

Subsequently the package is installed, inform ASP.NET that you're using the services of Cors. Open the Startup.cs file and locate the ConfigureServices() method. Call the AddCors() method on the services object equally shown in the following code snippet:

          public void ConfigureServices(IServiceCollection services) {     services.AddCors();     services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }                  

Gyre downward within the Startup.cs file and locate the Configure() method. Listing one shows you the code to add to permit the Web API to accept requests only from localhost:4200. Allow all HTTP methods such every bit GET, PUT, etc., by calling the method named AllowAnyMethod(). Allow whatever HTTP headers within the Web API phone call by invoking the method AllowAnyHeader(). Telephone call the app.UseCors() method prior to calling the app.UseMvc() method.

List 1: Phone call UseCors() from the Configure() method in the Startup.cs file.

          public void Configure(IApplicationBuilder app, IHostingEnvironment env) {     if (env.IsDevelopment())     {         app.UseDeveloperExceptionPage();     }     else     {         app.UseHsts();     }          app.UseCors(options => options.WithOrigins("http://localhost:4200").AllowAnyMethod().AllowAnyHeader());          app.UseHttpsRedirection();     app.UseMvc(); }                  

Try It Out

It'south a good thought to examination out your Web API project and ensure that it accepts requests as expected. Select Debug > Get-go Debugging from the Lawmaking menu to build the .NET Core Spider web API project and launch a browser. The browser will come up with a blank page. Type the following into the browser accost bar:

          http://localhost:5000/api/values                  

Press the Enter key to submit the request and you should see a string that looks like the following:

          ["value1","value2"]                  

Add a FileToUpload Course

A file you upload from your local difficult bulldoze has attributes that you should transmit to the Web service. These attributes are things like the file name, the file type, the size of the file, etc. You're going to too add some additional properties to agree the file contents as a Base64 encoded string and a byte array. Create a \Models folder in the root of your Web API project. Add a new file named FileToUpload.cs into this folder. Add the lawmaking shown in the following code snippet to this file:

          using System;  public class FileToUpload {     public string FileName { get; set; }     public string FileSize  { get; gear up; }     public string FileType  { go; set; }     public long LastModifiedTime  { go; set; }     public DateTime LastModifiedDate { go; set; }     public string FileAsBase64 { get; set; }     public byte[] FileAsByteArray { get; set; } }                  

Add together a FileUpload Controller

Obviously, you lot demand a controller class to which you lot send the file. Delete the ValuesController.cs file from the \Controllers binder. Add a file named FileUploadController.cs into the \Controllers folder. Add the lawmaking shown beneath to this file:

          using Organization; using System.IO; using Microsoft.AspNetCore.Mvc;  [Route("api/[controller]")] public class FileUploadController : Controller {     const string FILE_PATH = @"D:\Samples\";          [HttpPost]     public IActionResult Mail service([FromBody]FileToUpload theFile)      {         return Ok();     } }                  

The above code creates the route for the FileUploadController, sets upward a constant with the path to a binder on your local difficult drive that your Web projection has permissions to write to, and sets upwardly the Post() method to which you ship an object of the type FileToUpload.

Instead of the hard-coded constant for the file path to write the uploaded file to, y'all should take advantage of the ASP.Cyberspace Core configuration system to shop the path. For this article, I wanted to go on the code simple, and used a constant.

Create a Unique File Name

First creating the code within the Mail() method at present. Just above the return Ok(); line, define a new variable named filePathName. Create the total path and file proper name to store the uploaded file into. Use the FILE_PATH abiding, followed by the FileName property, without the file extension, from the file object uploaded. To provide some uniqueness to the file name, add on the current date and time. Remove whatever characters that aren't valid for a file by using the Replace() method. Terminate the file proper noun by adding on the file extension from the file object uploaded.

          var filePathName = FILE_PATH + Path.GetFileNameWithoutExtension(theFile.FileName) + "-" +     DateTime.Now.ToString().Replace("/", "").Supersede(":", "").Supercede(" ", "") +     Path.GetExtension(theFile.FileName);                  

If yous're going to accept multiple users uploading files at the aforementioned time, you might besides want to add together a session ID, a GUID, or the user'due south name to the file name and so you won't get whatever proper name collisions.

Remove File Type

In the Athwart code y'all're going to write, you're going to be reading the file from the file arrangement using the FileReader form. This grade is only bachelor on newer browsers. The FileReader class reads the file from disk as a Base64-encoded string. At the beginning of this Base64-encoded string is the blazon of file read from the disk followed by a comma. The residual of the file contents are later on the comma. Next is a sample of what the contents of the file uploaded look like.

          "information:image/jpeg;base64,/9j/4AAQSkZJRgABAg..."                  

Write the following lawmaking to strip off the file type. Check to ensure that the file blazon is there so y'all don't get an fault by passing a negative number to the Substring() method.

          if (theFile.FileAsBase64.Contains(",")) {     theFile.FileAsBase64 = theFile.FileAsBase64.Substring(theFile.FileAsBase64.IndexOf(",") + i); }                  

Convert to Binary

Don't store the file uploaded as a Base64-encoded string. You lot desire the file to be useable on the server just like it was on the user's difficult drive. Convert the file data into a byte array using the FromBase64String() method on the .NET Catechumen grade. Store the results of calling this method in to the FileAsByteArray property on the FileToUpload object.

          theFile.FileAsByteArray = Convert.FromBase64String(theFile.FileAsBase64);                  

Write to a File

You're finally ready to write the file to disk on your server. Create a new FileStream object and pass the byte array to the Write() method of this method. Pass in a zero as the second parameter and the length of the byte array as the 3rd parameter so the complete file is written to disk.

          using (var fs = new FileStream(filePathName, FileMode.CreateNew))  {     fs.Write(theFile.FileAsByteArray, 0, theFile.FileAsByteArray.Length); }                  

I've purposefully left out whatsoever mistake handling, just y'all should add some into this method. I'll leave that as an practise for you to do. Let's motion on and create an Athwart project to upload files to this Web API.

Build an Athwart Upload Project

Build an Athwart project from within VS Lawmaking by opening the terminal window. Navigate to the folder y'all created at the beginning of this article. For example, I navigate to the folder D:\Samples\FileUploadSample. Enter the post-obit Angular CLI command in the terminal window to create a new Angular application:

          ng new FileUploadAngular                  

Add Web API Project to Workspace

One time the project is created, add the newly created folder named FileUploadAngular to VS Lawmaking. Select the File > Add Folder to Workspace... menu item, as shown in Figure 4.

Figure 4: Add the Web API project to VS Code.
Figure 4: Add the Web API project to VS Code.

Choose the FileUploadAngular folder as shown in Effigy 5 and click the Add together button.

Figure 5: Add the folder where the Angular project is located to VS Code.
Figure 5: Add the folder where the Angular project is located to VS Code.

Yous should now meet ii projects within VS Lawmaking, as shown in Figure 6.

Figure 6: Add the Web API folder to VS Code
Figure half dozen: Add the Web API folder to VS Code

Salvage the Workspace

Click File > Save Workspace As... and give it the name FileUploadSampleApp. Click the Save button to store this new workspace file on disk. From at present on, y'all may always open this application by double-clicking on the FileUploadSampleApp.code-workspace file.

Create FileToUpload Class

Just every bit y'all created a FileToUpload class in C# to hold the various attributes about a file, create the same class in Angular. Go back into the terminal window and navigate to the FileUploadAngular folder, equally shown in Figure 7.

Figure 7: Add the Web API folder to VS Code.
Figure 7: Add together the Spider web API folder to VS Code.

Apply the Angular CLI control ng g cl to create a new class that represents a file to upload. You lot tin can create a folder named file-upload at the same fourth dimension you create the FileToUpload class. The FileToUpload form will exist created in this new folder.

          ng g cl file-upload/fileToUpload                  

Open up the newly generated file-to-upload.ts file and add the lawmaking shown next. Notice that this class has the same belongings names equally the C# class you created earlier. Using the same property names allows you to post this object to the Web API and the data contained in each matching property proper name is automatically mapped to the properties in the C# class.

          export class FileToUpload {     fileName: string = "";     fileSize: number = 0;     fileType: string = "";     lastModifiedTime: number = 0;     lastModifiedDate: Engagement = null;     fileAsBase64: string = ""; }                  

Create File Upload Service

Equally with whatever Angular application, separate the logic that communicates with a Web API into an Athwart service class. You lot can create a new service using the Angular CLI command ng thousand s. In the Integrated Final window, enter the post-obit command to create a FileUploadService class:

          ng grand south file-upload/fileUpload -m app.module                  

Open the newly generated file-upload.service.ts file and add together the import statements in the next snippet. The HttpClient and HttpHeaders classes that are imported are used to send an HTTP request. The Observable class from the RxJS library is ordinarily used equally a render value from service calls. The FileToUpload class is needed so y'all can laissez passer the information almost the file to upload to the Web API.

          import { HttpClient, HttpHeaders } from '@angular/mutual/http'; import { Observable } from 'rxjs'; import { FileToUpload } from './file-to-upload';                  

Add ii constants just beneath the import statements. The first constant is the path to the FileUpload controller that you created earlier in this article. The 2d constant is a header used to post JSON data to the Spider web API.

          const API_URL = "http://localhost:5000/api/FileUpload/"; const httpOptions = {     headers: new HttpHeaders({         'Content-Type': 'application/json'     }) };                  

The HttpClient class needs to be injected into this service course to post the file information to the Web API method. Modify the constructor of the FileUploadService class to tell the Dependency Injection system of Angular to pass in an instance of the HttpClient class.

          constructor(individual http: HttpClient) { }                  

Add together a method named uploadFile() to which you laissez passer an example of the FileToUpload grade. This class is built using properties from a File object retrieved from the user via an <input type="file"> element. Telephone call the postal service() method on the HttpClient course passing in the URL where the controller is located, the file object to post and the HTTP header abiding.

          uploadFile(theFile: FileToUpload) : Observable<whatsoever> {     return this.http.post<FileToUpload>(API_URL, theFile, httpOptions); }                  

File Upload Component

It's now time to build the HTML page that prompts the user for a file from their local drive that they wish to upload to the Spider web server. Create an Angular component using the Athwart CLI control ng g c. This command builds a .css, .html, and .ts file from which y'all build the file upload page. In the terminal window, enter the following command:

          ng m c fileUpload                  

Open the file-upload.component.ts file and add together two import statements for the FileUploadService and the FileToUpload class.

          import { FileUploadService } from './file-upload.service'; import { FileToUpload } from './file-to-upload';                  

You lot should limit the maximum size of file that a user may endeavor to upload. The files are going to exist Base64 encoded prior to sending. When you Base64-encode something, it grows larger than the original size. And then it'due south all-time to limit the size of the file to upload. Add a constant just beneath the import statements and prepare a limit of i megabyte. Feel free to play with different file sizes to meet what works with your situation.

          // Maximum file size allowed to be uploaded = 1MB const MAX_SIZE: number = 1048576;                  

Y'all need to add 2 public properties to FileUploadComponent class. The first property, theFile, holds an instance of a file object returned from the file upload object. There's no equivalent of the HTML file object in Athwart, so you lot must utilize the whatever information type. The second holding, messages, is used to display 1 or more than letters to the user.

          theFile: any = null; messages: cord[] = [];                  

The FileUploadComponent class creates an case of a FileToUpload class from the information contained in the theFile property. It then passes this object to the FileUploadService to upload the file to the server. Inject the FileUploadService into the constructor of the FileUploadComponent.

          constructor(private uploadService: FileUploadService) { }                  

Get File Data from Alter Event

When the user selects a file from their file system, the change result is fired on the file input element. Y'all're going to respond to this change issue and phone call a method named onFileChange() in your component. Write this code as shown hither:

          onFileChange(effect) {     this.theFile = null;     if (event.target.files && event.target.files.length > 0) {         // Don't allow file sizes over 1MB         if (event.target.files[0].size < MAX_SIZE) {             // Set up theFile property             this.theFile = event.target.files[0];         }         else {             // Brandish error bulletin             this.messages.button("File: " + consequence.target.files[0].proper name + " is too large to upload.");         }     } }                  

In the onFileChange() method an statement, named upshot, is passed in by the file input chemical element. Yous bank check the target.files belongings of this argument to come across if the user selected a file. If a file is selected, check the size holding to make sure it'southward less than the size you lot placed into the MAX_SIZE abiding. If the file meets the size requirement, you think the commencement element in the files assortment and assign it to the theFile property. If the file exceeds the size requirement, push a bulletin onto the messages array to inform the user of the file name that's in mistake.

Read and Upload File

Add together a private method named readAndUploadFile() to the FileUploadComponent class equally shown in List ii. Pass the theFile holding to this method. Although yous could merely access theFile belongings from this method, later in this article, you're going to need to pass a file object to this method to support multiple file uploads. Create a new instance of a FileToUpload class and ready the properties from the file object retrieved from the file input element.

Listing ii: Read a file using the FileReader grade

          private readAndUploadFile(theFile: whatsoever) {     allow file = new FileToUpload();          // Prepare File Information     file.fileName = theFile.name;     file.fileSize = theFile.size;     file.fileType = theFile.type;     file.lastModifiedTime = theFile.lastModified;     file.lastModifiedDate = theFile.lastModifiedDate;          // Use FileReader() object to get file to upload     // Notation: FileReader only works with newer browsers     allow reader = new FileReader();          // Setup onload event for reader     reader.onload = () => {         // Store base64 encoded representation of file         file.fileAsBase64 = reader.result.toString();                  // POST to server         this.uploadService.uploadFile(file).subscribe(resp => {              this.letters.push("Upload complete"); });     }          // Read the file     reader.readAsDataURL(theFile); }                  

Use the FileReader object from HTML 5 to read the file from disk into retentiveness. Notation that this FileReader object only works with modern browsers. Create a new instance of a FileReader form, and setup an onload() result that's called after the file has been loaded by the readAsDataUrl() method.

The readAsDataUrl() reads the contents and returns the contents as a Base64 encoded cord. Within the onload() event, you go the file contents in the result holding of the reader. Place the contents into the fileAsBase64 holding of the FileToUpload object. Call the uploadFile() method on the FileUploadService course, passing in this FileToUpload object. Upon successfully uploading the file, push button the message "Upload Complete" onto the messages array to accept it displayed to the user. The readAndUploadFile() method is called in response to the Upload File button's click event.

          uploadFile(): void {     this.readAndUploadFile(this.theFile); }                  

The File Upload HTML

Open up the file-upload.component.html file and delete all of the HTML within that file. Add the HTML shown in Listing three to create the page shown back in Figure i. In the <input type="file" ...> element, y'all run into that the alter consequence is mapped to the onFileChange() method yous wrote earlier. The Upload File button is disabled until the theFile property is not nil. When this push button is clicked on, the uploadFile() method is chosen to kickoff the upload process.

Listing 3: Use an input type of file to allow the user to select a file to upload

          <h1>File Upload</h1>  <label>Select a File (&lt; 1MB)</label><br/> <input type="file" (change)="onFileChange($event)" /><br/> <button (click)="uploadFile()" [disabled]="!theFile">Upload File</push><br/><br/>  <!-- ** BEGIN: INFORMATION MESSAGE AREA ** --> <div *ngIf="letters.length > 0">     <span *ngFor="let msg of letters">         {{msg}}         <br />         </span>     </div>     <!-- ** Cease: Data MESSAGE AREA ** -->                  

After the input and push button elements, you see another <div> chemical element that's only displayed when there are messages within the letters array. Each message is displayed inside a <bridge> element. These messages can display error or success letters to the user.

Change App Module

Because yous're using the HttpClient class in your FileUploadService course, you demand to inform Athwart of your intention to utilize this class. Open the app.module.ts file and add a reference to the HttpClientModule to the imports property. Subsequently typing the comma, and HttpClientModule, striking the Tab cardinal, or apply the light bulb in VS Code, to add together the appropriate import argument to the app.module.ts file.

          imports: [BrowserModule, HttpClientModule],                  

Alter App Component HTML

The app.component.html file is the first file displayed from the alphabetize.html page. Angular generates some default HTML into this file. Delete all the code inside this file and add the code shown beneath to brandish your file upload component.

          <app-file-upload></app-file-upload>                  

Attempt information technology Out

Start the Web API project, if information technology's not already running, past selecting Debug > Kickoff Debugging from the VS Code menu. Open a terminal window in the FileUploadAngular folder and start your Angular application using the Angular CLI control npm get-go, equally shown in the adjacent snippet:

          npm offset                  

Open your browser and enter localhost:4200 into the accost bar. Yous should see a Spider web folio that looks like Figure 1. Click on the Cull File button and select a file that's less than one megabyte in size. After yous select a file, the name of that file appears to the right of this button, and the Upload File button becomes enabled. Click the Upload File button and subsequently merely a second or two, an "Upload Complete" message should exist displayed. Check the binder where you specified to write the files and yous should encounter the file proper noun you selected followed by today's date. Open the file and ensure that all the file contents were written correctly.

Upload Multiple Files

The HTML file input blazon can upload multiple files past adding the multiple aspect to the input element. Each file to upload must exist nether one megabyte in size, but yous may select as many files as you lot wish. Open the file-upload.component.html file and modify the HTML shown in bold below:

          <label>Select a File(s) (&lt; 1MB)</label><br/> <input type="file" (modify)="onFileChange($event)" multiple="multiple" /><br/> <push (click)="uploadFile()" [disabled]="!theFiles.length">Upload {{theFiles.length}} File(s)</button>                  

Open the file-upload.component.ts file and locate the following line of code:

          theFile: any = aught;                  

Modify this from a unmarried object to an array of file objects.

          theFiles: any[] = [];                  

Modify the onFileChange() method then information technology looks similar Listing iv. Finally, alter the uploadFile() method to loop through the listing of file objects selected. Each time through the loop, pass the current instance of the file object retrieved from the file input type to the readAndUploadFile() method. You now understand why it was of import for you to pass a file object to the readAndUploadFile() method.

          uploadFile(): void {     for (let alphabetize = 0; index < this.theFiles.length; index++) {         this.readAndUploadFile(this.theFiles[index]);     } }                  

Listing 4: Modify the onFileChange() method to support multiple files

          onFileChange(event) {     this.theFiles = [];          // Any file(s) selected from the input?     if (result.target.files && event.target.files.length > 0) {         for (permit alphabetize = 0; alphabetize < event.target.files.length; index++) {             permit file = issue.target.files[alphabetize];             // Don't let file sizes over 1MB             if (file.size < MAX_SIZE) {                 // Add file to list of files                 this.theFiles.push(file);             }             else {                 this.messages.push("File: " + file.proper noun + " is too big to upload.");             }         }     } }                  

Try it Out

Save all your changes. Go back to the browser and y'all may now select multiple files. Select a few files, click the Upload Files button and ensure that all files are uploaded to the appropriate folder.

Summary

In this article, you learned to upload minor files from your client Spider web application to a server using Angular and a .Net Cadre Spider web API projection. On the server-side, yous demand to make sure that you enable CORS to laissez passer information from 1 domain to some other. On the client-side, you're using the FileReader class to read data from the user'due south file system. The technique illustrated in this commodity should only exist used for small files, equally it would take too long to read a big file from deejay, and too long to ship to the server. During this time, you tin can't provide feedback to the user and you won't be able to display a percentage uploaded. There are several good open up-source libraries for uploading large files to the server that practice provide feedback as the upload procedure is happening.