Azure

CRUD Operations With Azure Blob Storage, .NET 6, And Angular 14

Introduction 

Microsoft has launched a brand new library, Azure.Storage.Blobs for dealing with the Blob operations. I’ve already written an article about Blob operations a number of years in the past on C# Nook.  

Above article mentioned based mostly on an utility which was constructed with .NET Core 2.2 and Angular 8. That utility was referring to the library WindowsAzure.Storage which was now marked as out of date by Microsoft.  

Since each .NET Core and Angular variations are modified usually and lately got here with the most recent variations 6 and 14 respectively with drastic adjustments, I’ve determined to create a brand new article which can use .NET 6 and Angular 16. I’ll clarify all of the steps one after the other.  

We are able to create an Azure storage account first.  

Create Azure storage account on Azure Portal 

We are able to log into Azure portal and create a brand new Azure Blob storage. 

We are able to select any present useful resource group or create a brand new useful resource group. We should select a area and provide a legitimate account identify. 

We are able to click on the Create button to create our new Blob storage. The brand new useful resource will probably be deployed inside a number of moments. 

At the moment, there are 4 forms of companies out there in Blob storage. 

Blob service, File service, Queue service and Desk service are the companies. On this article, we will probably be discussing about Blob service. 

Create ASP.NET Core 6.Zero Internet API utilizing Visual Studio 2022 

We are able to create our Internet API mission utilizing Visual Studio 2022. We should select the ASP.NET Core template and .NET 6.Zero framework. 

Our new mission will probably be created with default Swagger documentation quickly. 

We are able to set up the Azure.Storage.Blobs library utilizing NuGet bundle supervisor. 

We are able to create a configuration contained in the appsettings.json file and add our Azure Storage account connection string and container identify.  

You may get the storage connection from the Entry keys part of storage account. Click on the Present keys button to get the connection string. 

Please observe that, now we have not but created a Container contained in the Blob storage. Our utility will robotically create a container if it doesn’t exist. We are going to write the code for that later.  

We are able to put the connection string and container identify contained in the appsettings.json file.  

appsettings.json 

{
  "Logging": {
    "LogLevel": {
      "Default": "Data",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "MyConfig": {
    "StorageConnection": "DefaultEndpointsProtocol=https;AccountName=sarathblobstorage;AccountKey=IMGCgU7hTXUO9hAzRd9YZXCfGy/QWFX048+301cNpxVOGkoRV+9+Lad/tJ5XjDfR2pUALEXbSk2J+AStloNliQ==;EndpointSuffix=core.home windows.internet",
    "ContainerName": "sarathcontainer"
  }
}

We’d like one interface and repair class to offer CRUD operations. This service class can be utilized from our API controller.  

We are able to create our IBlobStorage interface with the tactic definitions beneath. 

IBlobStorage.cs 

namespace AzureBlobDotNET6.Helpers
{
    public interface IBlobStorage
    {
        public Job<Checklist<string>> GetAllDocuments(string connectionString, string containerName);
        Job UploadDocument(string connectionString, string containerName, string fileName, Stream fileContent);
        Job<Stream> GetDocument(string connectionString, string containerName, string fileName);
        Job<bool> DeleteDocument(string connectionString, string containerName, string fileName);
    }
}

We are able to create BlobStorage service class and implement above IBlobStorage interface. 

BlobStorage.cs 

utilizing Azure.Storage.Blobs;
utilizing Azure.Storage.Blobs.Fashions;

namespace AzureBlobDotNET6.Helpers
{
    public class BlobStorage : IBlobStorage
    {
        public async Job<Checklist<string>> GetAllDocuments(string connectionString, string containerName)
        {
            var container = BlobExtensions.GetContainer(connectionString, containerName);

            if (!await container.ExistsAsync())
            {
                return new Checklist<string>();
            }

            Checklist<string> blobs = new();

            await foreach (BlobItem blobItem in container.GetBlobsAsync())
            {
                blobs.Add(blobItem.Identify);
            }
            return blobs;
        }

        public async Job UploadDocument(string connectionString, string containerName, string fileName, Stream fileContent)
        {
            var container = BlobExtensions.GetContainer(connectionString, containerName);
            if (!await container.ExistsAsync())
            {
                BlobServiceClient blobServiceClient = new(connectionString);
                await blobServiceClient.CreateBlobContainerAsync(containerName);
                container = blobServiceClient.GetBlobContainerClient(containerName);
            }

            var bobclient = container.GetBlobClient(fileName);
            if (!bobclient.Exists())
            {
                fileContent.Place = 0;
                await container.UploadBlobAsync(fileName, fileContent);
            }
            else
            {
                fileContent.Place = 0;
                await bobclient.UploadAsync(fileContent, overwrite: true);
            }
        }
        public async Job<Stream> GetDocument(string connectionString, string containerName, string fileName)
        {
            var container = BlobExtensions.GetContainer(connectionString, containerName);
            if (await container.ExistsAsync())
            {
                var blobClient = container.GetBlobClient(fileName);
                if (blobClient.Exists())
                {
                    var content material = await blobClient.DownloadStreamingAsync();
                    return content material.Worth.Content material;
                }
                else
                {
                    throw new FileNotFoundException();
                }
            }
            else
            {
                throw new FileNotFoundException();
            }

        }

        public async Job<bool> DeleteDocument(string connectionString, string containerName, string fileName)
        {
            var container = BlobExtensions.GetContainer(connectionString, containerName);
            if (!await container.ExistsAsync())
            {
                return false;
            }

            var blobClient = container.GetBlobClient(fileName);

            if (await blobClient.ExistsAsync())
            {
                await blobClient.DeleteIfExistsAsync();
                return true;
            }
            else
            {
                return false;
            }
        }
    }

    public static class BlobExtensions
    {
        public static BlobContainerClient GetContainer(string connectionString, string containerName)
        {
            BlobServiceClient blobServiceClient = new(connectionString);
            return blobServiceClient.GetBlobContainerClient(containerName);
        }
    }
}

We’d like a minor change within the Program.cs file to allow CORS (Cross Origin Useful resource Sharing). In order that we are able to ship Http requests from Angular utility with none points. We are able to additionally inject our BlobStorage service class and interface utilizing dependency injection.  

Program.cs 

utilizing AzureBlobDotNET6.Helpers;

var builder = WebApplication.CreateBuilder(args);

// Add companies to the container.

builder.Providers.AddControllers();
// Be taught extra about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Providers.AddEndpointsApiExplorer();
builder.Providers.AddSwaggerGen();

builder.Providers.AddScoped<IBlobStorage, BlobStorage>();

var MyAllowedOrigins = "_myAllowedOrigins";
builder.Providers.AddCors(choices =>
{
    choices.AddPolicy(MyAllowedOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://localhost:4200")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
});

var app = builder.Construct();

// Configure the HTTP request pipeline.
if (app.Atmosphere.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseAuthorization();

app.MapControllers();

app.UseCors(MyAllowedOrigins);

app.Run();

We are able to create our API controller AzureCRUDController and add the code beneath. 

AzureCRUDController.cs 

utilizing AzureBlobDotNET6.Helpers;
utilizing Microsoft.AspNetCore.Mvc;

namespace AzureBlobDotNET6.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class AzureCRUDController : ControllerBase
    {
        personal readonly IBlobStorage _storage;
        personal readonly string _connectionString;
        personal readonly string _container;

        public AzureCRUDController(IBlobStorage storage, IConfiguration iConfig)
        {
            _storage = storage;
            _connectionString = iConfig.GetValue<string>("MyConfig:StorageConnection");
            _container = iConfig.GetValue<string>("MyConfig:ContainerName");
        }

        [HttpGet("ListFiles")]
        public async Job<Checklist<string>> ListFiles()
        {
            return await _storage.GetAllDocuments(_connectionString, _container);
        }

        [Route("InsertFile")]
        [HttpPost]
        public async Job<bool> InsertFile([FromForm] IFormFile asset)
        {
            if (asset != null)
            {
                Stream stream = asset.OpenReadStream();
                await _storage.UploadDocument(_connectionString, _container, asset.FileName, stream);
                return true;
            }

            return false;
        }

        [HttpGet("DownloadFile/{fileName}")]
        public async Job<IActionResult> DownloadFile(string fileName)
        {
            var content material = await _storage.GetDocument(_connectionString, _container, fileName);
            return File(content material, System.Internet.Mime.MediaTypeNames.Utility.Octet, fileName);
        }

        [Route("DeleteFile/{fileName}")]
        [HttpGet]
        public async Job<bool> DeleteFile(string fileName)
        {
            return await _storage.DeleteDocument(_connectionString, _container, fileName);
        }

    }
}

We now have accomplished the API coding. Now we are able to create the Angular 14 mission. 

Create Angular 14 mission utilizing Angular CLI 

We are able to create a brand new Angular mission utilizing CLI. 

We’d like “bootstrap”, “font-awesome”, and “file-saver” libraries for this mission. We are able to set up these libraries utilizing npm command. 

npm set up bootstrap font-awesome file-saver 

To make use of file-saver library, typescript definitions could be put in moreover through: 

npm set up @sorts/file-saver 

We are able to import “bootstrap” and “font-awesome” libraries contained in the type.css class within the root folder of “src” folder. This manner, we are able to use these libraries in your complete mission with out additional references. 

types.css 

@import "~bootstrap/dist/css/bootstrap.css";  
@import "~font-awesome/css/font-awesome.css";  

Allow us to add “HttpClientModule” and “FormsModule” in app.module.ts file. We are going to use each these modules in our mission. 

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.element';

import { FormsModule } from '@angular/types';
import { HttpClientModule } from '@angular/frequent/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    HttpClientModule,  
    FormsModule  
  ],
  suppliers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

We are able to add a baseUrl variable contained in the setting.ts file. 

setting.ts 

export const setting = {
  manufacturing: false,
  baseUrl: 'http://localhost:5000/'
};

We are able to change the app.element.ts element file with the code beneath. 

app.element.ts 

import { Part, ElementRef, OnInit, ViewChild } from '@angular/core';
import { HttpClient } from '@angular/frequent/http';
import { setting } from 'src/environments/setting';
import * as saveAs from 'file-saver';

@Part({
  selector: 'app-root',
  templateUrl: './app.element.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(personal http: HttpClient) { }
  recordsdata: string[] = [];
  fileToUpload!: FormData;
  showLoader!: boolean;
  @ViewChild("fileUpload", { static: false }) fileUpload!: ElementRef;

  personal url = setting.baseUrl + 'api/azurecrud';

  ngOnInit(): void {
    this.showBlobs();
  }

  showBlobs() {
    this.showLoader = true;
    this.http.get<string[]>(this.url + '/listfiles')
      .subscribe({
        subsequent: (consequence) => {
          this.recordsdata = consequence
        },
        error: (err) => {
          console.error(err);
        },
        full: () => {
          this.showLoader = false;
        }
      });
  }

  onClick() {
    let fileUpload = this.fileUpload.nativeElement;
    fileUpload.onchange = () => {
      this.showLoader = true;
      const file = fileUpload.recordsdata[0];
      let formData: FormData = new FormData();
      formData.append("asset", file, file.identify);
      this.http.put up(this.url + '/insertfile', formData)
        .subscribe({
          subsequent: (response: any) => {
            if (response == true) {
              this.showBlobs();
            }
          },
          error: (err) => {
            console.error(err);
            this.showLoader = false;
          },
          full: () => {
          }
        });
    };
    fileUpload.click on();
  }

  downloadFile(fileName: string) {
    this.showLoader = true;
    return this.http.get(this.url + '/downloadfile/' + fileName, { responseType: "blob" })
      .subscribe({
        subsequent: (consequence: any) => {
          if (consequence.kind != 'textual content/plain') {
            var blob = new Blob([result]);
            let file = fileName;
            saveAs(blob, file);
          }
          else {
            alert('File not present in Blob!');
          }
        },
        error: (err) => {
          console.error(err);
        },
        full: () => {
          this.showLoader = false;
        }
      });
  }

  deleteFile(fileName: string) {
    var del = affirm('Are you positive wish to delete this file');
    if (!del) return;
    this.showLoader = true;
    this.http.get(this.url + '/deletefile/' + fileName)
      .subscribe({
        subsequent: (consequence: any) => {
          if (consequence != null) {
            this.showBlobs();
          }
        },
        error: (err) => {
          console.error(err);
          this.showLoader = false;
        },
        full: () => {
        }
      });
  }
}

We now have added all of the logic for file operations with Blob Storage on this element. 

Modify the corresponding HTML and CSS recordsdata for this element as properly. 

app.element.html 

<div type="text-align:heart; margin-top: 20px;">
  <h4>
    CRUD operations with Azure Blob Storage, .NET 6 and Angular 14
  </h4>
</div>

<div class="blobstorage-section">
  <i class="fa fa-plus fa-2x" type="cursor: pointer; shade: darkslateblue;" (click on)="onClick()"></i>&nbsp; Add new recordsdata to Blob
  <enter kind="file" #fileUpload id="fileUpload" identify="fileUpload" type="show:none;" (click on)="fileUpload.worth = ''"/>
  <desk type="margin-top: 20px;">
    <tr>
      <th class="column1">Uploaded Information</th>
      <th class="column2" type="text-align:heart;">Obtain</th>
      <th class="column3" type="text-align:heart;">Delete</th>
    </tr>
    <tr *ngFor="let file of recordsdata">
      <td class="column1">{{file}}</td>
      <td class="column2" type="text-align:heart;cursor: pointer;" (click on)="downloadFile(file)"><i class="fa fa-download"></i></td>
      <td class="column3" type="text-align:heart;" (click on)="deleteFile(file)">
        <img alt="Group Audit" src="https://www.c-sharpcorner.com/belongings/icon-download.png" />
      </td>
    </tr>
  </desk>
</div>
<div class="file-loader" *ngIf="showLoader">
  <div class="upload-loader">
    <div class="loader"></div>
  </div>
</div>


app.element.css 

/* Spin Begin*/

.file-loader {
    background-color:
        rgba(0, 0, 0, .5);
    overflow: hidden;
    place: fastened;
    high: 0;
    left: 0;
    proper: 0;
    backside: 0;
    z-index: 100000 !essential;
}

.upload-loader {
    place: absolute;
    width: 60px;
    peak: 60px;
    left: 50%;
    high: 50%;
    rework: translate(-50%, -50%);
}

.upload-loader .loader {
    border: 5px strong #f3f3f3 !essential;
    border-radius: 50%;
    border-top: 5px strong #005eb8 !essential;
    width: 100% !essential;
    peak: 100% !essential;
    -webkit-animation: spin 2s linear infinite;
    animation: spin 2s linear infinite;
}

@-webkit-keyframes spin {
    0% {
        -webkit-transform: rotate(0deg);
    }

    100% {
        -webkit-transform: rotate(360deg);
    }
}

@keyframes spin {
    0% {
        rework: rotate(0deg);
    }

    100% {
        rework: rotate(360deg);
    }
}

/* Spin Finish*/

.blobstorage-section {
    font-family: Calibri;
    box-shadow: Zero 1px 4px 0 #9b9b9b;
    background-color: #ffffff;
    margin: auto;
    margin-top: 50px;
    padding: 30px;
    width: 50%;
}

.column1 {
    width: 450px;
}

.column2 {
    width: 100px;
}

.column3 {
    width: 100px;
}

.blobstorage-section th {
    font-family: Calibri;
    font-size: 14px;
    font-weight: daring;
    font-style: regular;
    font-stretch: regular;
    line-height: 1.57;
    letter-spacing: regular;
    shade: #333333;
    border-right: 1px strong #d7d7d7;
    border-bottom: 1px strong #d7d7d7;
}

.blobstorage-section th i {
    font-family: Calibri;
    font-size: 16px;
}

.blobstorage-section tbody tr td {
    border-right: 1px strong #d7d7d7;
    border-bottom: 1px strong #d7d7d7;
}

.blobstorage-section tbody tr td a {
    font-family: Calibri;
    font-size: 15px;
    font-weight: regular;
    font-style: regular;
    font-stretch: regular;
    line-height: 1.2;
    letter-spacing: regular;
    shade: #0091da;
    text-decoration: underline;
}

.blobstorage-section tr td img:hover {
    cursor: pointer;
}

We now have accomplished the Angular mission as properly. We are able to run each ASP.NET Core mission and Angular mission, now. 

We are able to add new recordsdata by clicking + button on the display screen. 

You possibly can click on the Obtain and Delete button to carry out respective actions as properly. 

Conclusion 

On this put up, now we have seen learn how to add, checklist, obtain and take away recordsdata from Azure Blob storage utilizing ASP.NET 6.Zero API mission and now we have consumed these companies in an Angular 14 shopper mission. Hopefully, I’ve lined all the main points wanted for CRUD operations associated to Azure Blob storage. In case you are , you may obtain the supply code and test out of your facet.

Tags
Show More

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Close