Blazor File Uploads – to Azure Blob Storage

The Intro…

You might wonder, your an Architect, why are you posting blogs about coding? Good question I would say 🙂 Well, working as an Architect is my day job, but there are many different ideas I have in my head and some of them just require a bit more thought, where I might need to build a PoC, or sometimes like this post, I like to try out new frameworks, libraries or just build samples for fun. Having already coded for well over a decade in .NET, doesn’t stop me from wanting to keep up to date with my coding skills , especially C#, or trying out different architectural patterns to understand how they can be applied in software applications, hybrid architectures, native cloud architectures or help a project I am working on at a specific point in time. From a work perspective, all things Microsoft are highly important to me, especially since I consider myself lucky enough to have spent a fair bit of time working at Microsoft Canada, Microsoft UK and in Seattle at Microsoft HQ and at Microsoft conferences being trained and kept up to date on technologies, which were not always about Microsoft Azure of course.

Now back to this post…

With the recent updates announced at the .NET 2020 Conference last week, I decided to try out the new InputFile UI component in Blazor. Steve Sanderson had already posted a blog regarding a InputFile component here previously, in September 2019. This is now included with the launch of .NET 5. I’ve also been using the DevExpress Blazor UI Components library and used the <DXUpload/> UI component, which I highly recommend also.

In this post I wanted to try out the new native InputFile Control and upload some files to Azure blob storage for testing purposes. I am working on a project which will require the basic functionality of file uploads and downloads to and from Azure blob storage, I may add a future post about one of the new exciting projects I am working on.

Note: This is a very basic implementation for the purposes of this blog post, it is not at all production ready.

Nuget Packages

With any .NET project, the first thing you may want to do is to decide your approach on how you want to build out your components, whether or not you have existing shared libraries, component libraries, NuGet packages you work with regularly, or if you just like coding and building your own supporting toolsets and templates, you may not even need to include anything from the onset and just include packages whilst you are building out your solution in an agile way.

In my sample project, a Blazor Server application, called BlazorFileUploader, I used the following NuGet packages, Microsoft Azure.Storage.Blobs client library and DevExpress.Blazor.

Note: As I mentioned earlier in this article, I have been testing the DevExpress UI components <DXUpload> UI component, but it is not used in this post.

The code…

Appsettings.json

The following configuration was added for the Azure blob storage connection string. Of course, in an enterprise scenario, if your hosting your application in Azure App Service, you could utilise Azure KeyVault, which is recommended instead.

“Storage”: {
“StorageAccountConnectionString”: “DefaultEndpointsProtocol=https;AccountName=[YourStorageAccountName;AccountKey=YourStorageAccountKey==;EndpointSuffix=core.windows.net”,
},

Startup.cs

The the default configuration provider, the connection string is set in string within a config context class.

public class Startup
{
    public IConfiguration Configuration { get; }
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
        Config.AzureStorageConnectionString = Configuration.GetConnectionString("StorageAccountConnectionString");
    }
....

Config.cs

The AzureStorageConnectionString is set by the code above, so this can be used when the server upload is handled server side.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks;

namespace BlazorAzureStorageFileLoader.Data
{
    public class Config
    {
        public static string AzureStorageConnectionString { get; set; }

    }
}

New Razor page

A new Razor page is created to handle the file uploads, a bulk of the code, with the <InputFile/> component.

Note: I hard coded a container named “files” in the code. This is not necessary of course, you can build your own blob storage browser and even have methods to retrieve files and create containers.

@page "/fileloader"
@using System.IO
@using Azure.Storage.Blobs

<h4>Blob Storage File Loader</h4>

<InputFile OnChange="@UploadFiletoAzBlobStorage" />

<p>@status</p>

@if (fileSelected)
{
<p>
    <div class="spinner-border" /><h5>Uploading file...</h5>
</p>
}

@code {

    int count = 1;
    string status;
    bool fileSelected = false;
    private string localFileName { get; set; } = "Not Selected";

    private void UpdateStatus(ChangeEventArgs e)
    {
        status = e.Value.ToString();
        count = count +1 ;
        StateHasChanged();
    }


    async Task UploadFiletoAzBlobStorage(InputFileChangeEventArgs e)
    {
        var file = e.File;

        if (file != null)
        {

            fileSelected = true;

            string connectionString = Data.Config.AzureStorageConnectionString;

            // Nax File Size ~ 50MB
            long maxFileSize = 1024 * 1024 * 50;

            BlobContainerClient container = new BlobContainerClient(connectionString, "files");

            try
            {
                BlobClient blob = container.GetBlobClient(file.Name);
                using (Stream fs = file.OpenReadStream(maxFileSize))
                {
                    await blob.UploadAsync(fs);
                }
            }
            catch (Exception ex)
            {

            }
            finally
            {
                // Clean up after the test when we're finished
            }

            status = $"Finished loading {file.Size/1024/1024} MB from {file.Name}";
            fileSelected = false;
            StateHasChanged();
        }
        else
        {
            status = $"No file selected!";
        }
    }
}

Shared\NavMenu.razor

The navigation menu is updated to remove the default links and an update is made to the application display name. A new navigation link is added to the fileloader razor page.

div class="top-row pl-4 navbar navbar-dark">
    <a class="navbar-brand" href="">Azure Storage FileLoader</a>
    <button class="navbar-toggler" @onclick="ToggleNavMenu">
        <span class="navbar-toggler-icon"></span>
    </button>
</div>

<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
    <ul class="nav flex-column">
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </li>
        @*<li class="nav-item px-3">
            <NavLink class="nav-link" href="counter">
                <span class="oi oi-plus" aria-hidden="true"></span> Counter
            </NavLink>
        </li>
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fetchdata">
                <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
            </NavLink>
        </li>*@
        <li class="nav-item px-3">
            <NavLink class="nav-link" href="fileloader">
                <span class="oi oi-list-rich" aria-hidden="true"></span> File Loader
            </NavLink>
        </li>
        @*<li class="nav-item px-3">
            <NavLink class="nav-link" href="fileloader2">
                <span class="oi oi-list-rich" aria-hidden="true"></span> File Loader 2
            </NavLink>
        </li>*@
        @*<li class="nav-item px-3">
            <NavLink class="nav-link" href="Component Testing">
                <span class="oi oi-list-rich" aria-hidden="true"></span> File Loader 2
            </NavLink>
        </li>*@
    </ul>
</div>

@code {
    private bool collapseNavMenu = true;

    private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;

    private void ToggleNavMenu()
    {
        collapseNavMenu = !collapseNavMenu;
    }
}

The Test

The FileInput UI component is shown in the fileloader.razor page.

I choose a local file to upload.

The spinner is shown with the filename. The code which displays this in the razor page is highlighted below.

<p>@status</p>

@if (fileSelected)
{
<p>
    <div class="spinner-border" /><h5>Uploading file...</h5>
</p>
}

The file load to Azure blob storage is completed.

The blob is shown in the Azure storage account blob container.

That’s the most simple and quickest way to utilise the new <InputFile/> UI component in Blazor with .NET 5, with Azure blob storage.

.NET Core 3.1 to .NET 5.0

Today marks the launch of .NET 5.0, the unified platform framework which executes on desktop, web, cloud, mobile, gaming, IoT and AI platforms. Catch all the updates in the sessions at the .NET conference from November 10th 2020, through to November 12 2020, here. Ensure that you keep an eye on the support policy too, which is available here.

And more news, .NET 5.0 is available in Azure App Service, from today!

Useful Links:

Resources
• .NET Documentation https://aka.ms/dotnet-docs
• SDK Documentation https://aka.ms/dotnet-sdk-docs
• Release Notes https://aka.ms/dotnet5-release-notes
• Tutorials https://aka.ms/dotnet-tutorials
• Code Mag https://aka.ms/dotnet5-codemag

In addition, I’ve ensured Visual Studio 2019 is on the latest release, 16.8.0.