Tuesday, July 7, 2020

Upload, List, Download, and Delete Blob Files using ASP.NET Core and Angular 8



In this post, we will see how to upload, list, download, and delete files from Azure blob storage using ASP.NET Core Web API service and Angular 8 application.

Introduction

Blob storage is a feature in Microsoft Azure that lets developers store unstructured data in Microsoft’s cloud platform. This data can be accessed from anywhere in the world and can include audio, video and text files. Blobs are grouped into “containers” that are tied to user accounts. Blobs can be manipulated with .NET or .NET Core code.

In this article, we will see how to create an Azure blob storage and create services in ASP.NET Core Web API to upload, list, download, and delete files from Azure blob storage. We will create an Angular 8 application to perform these operations from client side.

Create Blob storage in Azure portal

It is easy to create a blob storage in Azure portal. Choose “Storage account” and give required information. Choose a unique name for storage account.

You can click “Review + create” button to validate and then click “Create” button to proceed. Currently, there are four type of services available in storage account.

We can choose “Blobs” and create a container to store our unstructured data.

We can choose access level as “Private”. So, that nobody will access without enough privileges. You can click “Access keys” tab and choose connection string and kept in a secured place for future usage. We will use this connection string in our ASP.NET Core project later.

Create Web API service in ASP.NET Core project

We can create a new ASP.NET Web API project in visual studio and create all services for file operations. 

Please choose API template

Our project will be ready in few moments. 

We can keep blob storage connection string and container name in appsettings.js file. We will fetch these values from settings file using a configuration class file. We can create a config file “MyConfig.cs” to access these configuration values.

MyConfig.cs

  1. namespace BlobStorageASP.NETCore
  2. {
  3. public class MyConfig
  4. {
  5. public string StorageConnection { get; set; }
  6. public string Container { get; set; }
  7. }
  8. }

I have created two properties for connection string and container name inside this class file.

We can add storage connection string and container name in appsettings.js file as well.

appsettings.js

  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Warning"
  5. }
  6. },
  7. "MyConfig": {
  8. "StorageConnection": "DefaultEndpointsProtocol=https;AccountName=sarathstorageaccount;AccountKey=G176lDXKsN1joI6yL9G/JuC4MCMdMSZNEJBzIYZnhcQsjkRd8MCfoxREiqguuo5kON2BcIpSHXSqnVDZ9+7OAQ==;EndpointSuffix=core.windows.net",
  9. "Container": "sarathcontainer"
  10. },
  11. "AllowedHosts": "*"
  12. }

Modify the Startup.cs class with below changes. We have added dependency for MyConfig class. We have enabled CORS in this class too. Because we will consume these Web API services in Angular 8 application.

Startup.cs

  1. using Microsoft.AspNetCore.Builder;
  2. using Microsoft.AspNetCore.Hosting;
  3. using Microsoft.AspNetCore.Mvc;
  4. using Microsoft.Extensions.Configuration;
  5. using Microsoft.Extensions.DependencyInjection;
  6. namespace BlobStorageASP.NETCore
  7. {
  8. public class Startup
  9. {
  10. public Startup(IConfiguration configuration)
  11. {
  12. Configuration = configuration;
  13. }
  14. public IConfiguration Configuration { get; }
  15. public void ConfigureServices(IServiceCollection services)
  16. {
  17. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  18. services.AddCors(c =>
  19. {
  20. c.AddPolicy("AllowOrigin", options => options.WithOrigins("http://localhost:4200"));
  21. });
  22. services.Configure<MyConfig>(Configuration.GetSection("MyConfig"));
  23. }
  24. public void Configure(IApplicationBuilder app, IHostingEnvironment env)
  25. {
  26. if (env.IsDevelopment())
  27. {
  28. app.UseDeveloperExceptionPage();
  29. }
  30. app.UseCors(options => options.WithOrigins("http://localhost:4200"));
  31. app.UseMvc();
  32. }
  33. }
  34. }

We can create “BlobStorage” controller now. We will use scaffolding template for creating this Web API controller.

Right click the controller folder and choose “Add New Scaffolded Item”

Choose “API” tab and select “API Controller with read/write actions” option.

Install “WindowsAzure.Storage” NuGet package in project.

Copy below code and paste inside this controller class.

BlobStorageController.cs

  1. using Microsoft.AspNetCore.Http;
  2. using Microsoft.AspNetCore.Mvc;
  3. using Microsoft.Extensions.Options;
  4. using Microsoft.WindowsAzure.Storage;
  5. using Microsoft.WindowsAzure.Storage.Blob;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.Threading.Tasks;
  9. namespace BlobStorageASP.NETCore.Controllers
  10. {
  11. [Route("api/[controller]")]
  12. [ApiController]
  13. public class BlobStorageController : ControllerBase
  14. {
  15. private readonly IOptions<MyConfig> config;
  16. public BlobStorageController(IOptions<MyConfig> config)
  17. {
  18. this.config = config;
  19. }
  20. [HttpGet("ListFiles")]
  21. public async Task<List<string>> ListFiles()
  22. {
  23. List<string> blobs = new List<string>();
  24. try
  25. {
  26. if (CloudStorageAccount.TryParse(config.Value.StorageConnection, out CloudStorageAccount storageAccount))
  27. {
  28. CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
  29. CloudBlobContainer container = blobClient.GetContainerReference(config.Value.Container);
  30. BlobResultSegment resultSegment = await container.ListBlobsSegmentedAsync(null);
  31. foreach (IListBlobItem item in resultSegment.Results)
  32. {
  33. if (item.GetType() == typeof(CloudBlockBlob))
  34. {
  35. CloudBlockBlob blob = (CloudBlockBlob)item;
  36. blobs.Add(blob.Name);
  37. }
  38. else if (item.GetType() == typeof(CloudPageBlob))
  39. {
  40. CloudPageBlob blob = (CloudPageBlob)item;
  41. blobs.Add(blob.Name);
  42. }
  43. else if (item.GetType() == typeof(CloudBlobDirectory))
  44. {
  45. CloudBlobDirectory dir = (CloudBlobDirectory)item;
  46. blobs.Add(dir.Uri.ToString());
  47. }
  48. }
  49. }
  50. }
  51. catch
  52. {
  53. }
  54. return blobs;
  55. }
  56. [HttpPost("InsertFile")]
  57. public async Task<bool> InsertFile(IFormFile asset)
  58. {
  59. try
  60. {
  61. if (CloudStorageAccount.TryParse(config.Value.StorageConnection, out CloudStorageAccount storageAccount))
  62. {
  63. CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();
  64. CloudBlobContainer container = blobClient.GetContainerReference(config.Value.Container);
  65. CloudBlockBlob blockBlob = container.GetBlockBlobReference(asset.FileName);
  66. await blockBlob.UploadFromStreamAsync(asset.OpenReadStream());
  67. return true;
  68. }
  69. else
  70. {
  71. return false;
  72. }
  73. }
  74. catch
  75. {
  76. return false;
  77. }
  78. }
  79. [HttpGet("DownloadFile/{fileName}")]
  80. public async Task<IActionResult> DownloadFile(string fileName)
  81. {
  82. MemoryStream ms = new MemoryStream();
  83. if (CloudStorageAccount.TryParse(config.Value.StorageConnection, out CloudStorageAccount storageAccount))
  84. {
  85. CloudBlobClient BlobClient = storageAccount.CreateCloudBlobClient();
  86. CloudBlobContainer container = BlobClient.GetContainerReference(config.Value.Container);
  87. if (await container.ExistsAsync())
  88. {
  89. CloudBlob file = container.GetBlobReference(fileName);
  90. if (await file.ExistsAsync())
  91. {
  92. await file.DownloadToStreamAsync(ms);
  93. Stream blobStream = file.OpenReadAsync().Result;
  94. return File(blobStream, file.Properties.ContentType, file.Name);
  95. }
  96. else
  97. {
  98. return Content("File does not exist");
  99. }
  100. }
  101. else
  102. {
  103. return Content("Container does not exist");
  104. }
  105. }
  106. else
  107. {
  108. return Content("Error opening storage");
  109. }
  110. }
  111. [Route("DeleteFile/{fileName}")]
  112. [HttpGet]
  113. public async Task<bool> DeleteFile(string fileName)
  114. {
  115. try
  116. {
  117. if (CloudStorageAccount.TryParse(config.Value.StorageConnection, out CloudStorageAccount storageAccount))
  118. {
  119. CloudBlobClient BlobClient = storageAccount.CreateCloudBlobClient();
  120. CloudBlobContainer container = BlobClient.GetContainerReference(config.Value.Container);
  121. if (await container.ExistsAsync())
  122. {
  123. CloudBlob file = container.GetBlobReference(fileName);
  124. if (await file.ExistsAsync())
  125. {
  126. await file.DeleteAsync();
  127. }
  128. }
  129. }
  130. return true;
  131. }
  132. catch
  133. {
  134. return false;
  135. }
  136. }
  137. }
  138. }

I have added code for upload, list, download, and delete files from Azure blob storage. All the codes are self-explanatory.

We have completed the API project coding part. We can check these services using Postman or any other tools.

Create Angular 8 Client application using CLI

Create a new angular 8 application using below CLI command.

ng new BlobStorageAngular8

We need “bootstrap”, “font-awesome” and “file-save” libraries in this project. We can install these libraries one by one.

We can import “bootstrap” and “font-awesome” libraries inside style.css class in root folder of “src” folder. So, that we can use these libraries in entire project without further references.

style.css

  1. /* You can add global styles to this file, and also import other style files */
  2. @import "~bootstrap/dist/css/bootstrap.css";
  3. @import "~font-awesome/css/font-awesome.css";

We can add “HttpClientModule” and “FormsModule” in app.module.ts file. We will use both these modules in our project.

app.module.ts

  1. import { BrowserModule } from '@angular/platform-browser';
  2. import { NgModule } from '@angular/core';
  3. import { AppComponent } from './app.component';
  4. import { HttpClientModule } from '@angular/common/http';
  5. import { FormsModule } from '@angular/forms';
  6. @NgModule({
  7. declarations: [
  8. AppComponent
  9. ],
  10. imports: [
  11. BrowserModule,
  12. HttpClientModule,
  13. FormsModule
  14. ],
  15. providers: [],
  16. bootstrap: [AppComponent]
  17. })
  18. export class AppModule { }

We can modify the app.component.ts component file with below code.

app.component.ts

  1. import { Component, OnInit } from '@angular/core';
  2. import { HttpClient } from '@angular/common/http';
  3. import { saveAs } from 'file-saver';
  4. declare var require: any;
  5. @Component({
  6. selector: 'app-root',
  7. templateUrl: './app.component.html',
  8. styleUrls: ['./app.component.css']
  9. })
  10. export class AppComponent implements OnInit {
  11. constructor(private http: HttpClient) { }
  12. files: string[] = [];
  13. fileToUpload: FormData;
  14. fileUpload: any;
  15. fileUpoadInitiated: boolean;
  16. fileDownloadInitiated: boolean;
  17. private baseUrl = 'http://localhost:4000/api/blobstorage';
  18. ngOnInit(): void {
  19. this.showBlobs();
  20. }
  21. showBlobs() {
  22. this.http.get<string[]>(this.baseUrl + '/listfiles').subscribe(result => {
  23. this.files = result;
  24. }, error => console.error(error));
  25. }
  26. addFile() {
  27. if (!this.fileUpoadInitiated) {
  28. document.getElementById('fileUpload').click();
  29. }
  30. }
  31. handleFileInput(files: any) {
  32. let formData: FormData = new FormData();
  33. formData.append("asset", files[0], files[0].name);
  34. this.fileToUpload = formData;
  35. this.onUploadFiles();
  36. }
  37. onUploadFiles() {
  38. if (this.fileUpoadInitiated) {
  39. return;
  40. }
  41. this.fileUpoadInitiated = true;
  42. if (this.fileToUpload == undefined) {
  43. this.fileUpoadInitiated = false;
  44. return false;
  45. }
  46. else {
  47. return this.http.post(this.baseUrl + '/insertfile', this.fileToUpload)
  48. .subscribe((response: any) => {
  49. this.fileUpoadInitiated = false;
  50. this.fileUpload = '';
  51. if (response == true) {
  52. this.showBlobs();
  53. }
  54. else {
  55. alert('Error occured!');
  56. this.fileUpoadInitiated = false;
  57. }
  58. },
  59. err => console.log(err),
  60. );
  61. }
  62. }
  63. downloadFile(fileName: string) {
  64. this.fileDownloadInitiated = true;
  65. return this.http.get(this.baseUrl + '/downloadfile/' + fileName, { responseType: "blob" })
  66. .subscribe((result: any) => {
  67. if (result.type != 'text/plain') {
  68. var blob = new Blob([result]);
  69. let saveAs = require('file-saver');
  70. let file = fileName;
  71. saveAs(blob, file);
  72. this.fileDownloadInitiated = false;
  73. }
  74. else {
  75. this.fileDownloadInitiated = false;
  76. alert('File not found in Blob!');
  77. }
  78. }
  79. );
  80. }
  81. deleteFile(fileName: string) {
  82. var del = confirm('Are you sure want to delete this file');
  83. if (!del) return;
  84. this.http.get(this.baseUrl + '/deletefile/' + fileName).subscribe(result => {
  85. if (result != null) {
  86. this.showBlobs();
  87. }
  88. }, error => console.error(error));
  89. }
  90. }

We have added all the logic for file operations with Blob storage in this component.

Modify the corresponding html and css files for this component as well.

app.component.html

  1. <div style="text-align:center; margin-top: 20px;">
  2. <h4>
  3. Upload, Download and Delete Blob Files using ASP.NET Core and Angular 8
  4. </h4>
  5. </div>
  6. <div class="blobstorage-section">
  7. <i class="fa fa-plus fa-2x" style="cursor: pointer; color: darkslateblue;" (click)="addFile()"></i> Add new files to Blob
  8. <input style="display: none;" type="file" id="fileUpload" #selectedFile [(ngModel)]="fileUpload" (click)="selectedFile.value = null" value="" (change)="handleFileInput($event.target.files)" />
  9. <table style="margin-top: 20px;">
  10. <tr>
  11. <th class="column1">Uploaded Files</th>
  12. <th class="column2" style="text-align:center;">Download</th>
  13. <th class="column3" style="text-align:center;">Delete</th>
  14. </tr>
  15. <tr *ngFor="let file of files">
  16. <td class="column1">{{file}}</td>
  17. <td class="column2" style="text-align:center;cursor: pointer;" (click)="downloadFile(file)"><i class="fa fa-download"></i></td>
  18. <td class="column3" style="text-align:center;" (click)="deleteFile(file)">
  19. <img alt="Group Audit" src="../assets/icon-download.png" />
  20. </td>
  21. </tr>
  22. </table>
  23. </div>

app.component.css

  1. .blobstorage-section {
  2. font-family: Calibri;
  3. box-shadow: 0 1px 4px 0 #9b9b9b;
  4. background-color: #ffffff;
  5. margin: auto;
  6. margin-top: 50px;
  7. padding: 30px;
  8. width: 50%;
  9. }
  10. .column1{
  11. width:450px;
  12. }
  13. .column2{
  14. width:100px;
  15. }
  16. .column3{
  17. width:100px;
  18. }
  19. .blobstorage-section th {
  20. font-family: Calibri;
  21. font-size: 14px;
  22. font-weight: bold;
  23. font-style: normal;
  24. font-stretch: normal;
  25. line-height: 1.57;
  26. letter-spacing: normal;
  27. color: #333333;
  28. border-right: 1px solid #d7d7d7;
  29. border-bottom: 1px solid #d7d7d7;
  30. }
  31. .blobstorage-section th i {
  32. font-family: Calibri;
  33. font-size: 16px;
  34. }
  35. .blobstorage-section tbody tr td {
  36. border-right: 1px solid #d7d7d7;
  37. border-bottom: 1px solid #d7d7d7;
  38. }
  39. .blobstorage-section tbody tr td a {
  40. font-family: Calibri;
  41. font-size: 15px;
  42. font-weight: normal;
  43. font-style: normal;
  44. font-stretch: normal;
  45. line-height: 1.2;
  46. letter-spacing: normal;
  47. color: #0091da;
  48. text-decoration: underline;
  49. }
  50. .blobstorage-section tr td img:hover {
  51. cursor: pointer;
  52. }

We have completed the entire project. Run both ASP.NET Core project and Angular project now.

Click (+) button to add new files.

You can download/delete these files using corresponding buttons in the grid.

Conclusion

In this post, we have seen how to upload, list, download and delete files from Azure Blob storage using ASP.NET Core API project and we have consumed these services in an Angular 8 client project. You can download the source code and check from your side. Please feel free to contact me with your valuable comments on this article.

ASP.NET Core Source Code on GitHub

Angular 8 Source Code on GitHub

 


No comments:

Post a Comment

Free hosting web sites and features -2024

  Interesting  summary about hosting and their offers. I still host my web site https://talash.azurewebsites.net with zero cost on Azure as ...