In our Blazor WebAssembly series, we have already talked about File Upload with Blazor and Web API. In that article, we’ve learned how to upload files to local storage. Of course, there are other places we can upload our files to. So, in this article, we are going to learn how to upload files to Azure blob storage using Blazor WebAssembly and ASP.NET Core Web API.

To download the source code for this article, you can visit the File Upload to Azure repository.

If you want to learn more about Blazor WebAssembly, we strongly suggest visiting our Blazor WebAssembly series of articles, where you can read about Blazor WebAssembly development, authentication, authorization, JSInterop, and other topics as well.

We are going to divide this article into the following sections:

Let’s start.

Creating Azure Storage

The first thing we have to do is to navigate and sign in to Azure Portal where we can create our storage. If we don’t have a subscription we can create a free subscription account on the dashboard.

Next, we are going to create a new storage account service:

 Azure Storage Accounts Service

After choosing the Storage accounts service, we have to click the Add button to create an account. We have to go through several steps to successfully create a storage account. That said, let’s first populate the Basic options:

Basics for creating azure storage account

For Networking and Data protection options, we are not going to change a thing.

Then, in the Advanced menu, we are going to select disabled for the Secure Transfer Required option. All the other options will stay as-is.

After these steps, we are going to click the Next button two times until our validation passes:

Validation passes for azure storage accounts

Finally, we can click the Create button, and after some time our deployment will be completed:

Deployment complete for azure storage account

Let’s click the Go to resource button to see our storage. There, we can click the Access keys settings (menu on the left) and inspect our keys and connection string. We are going to leave it open because we are going to need it soon enough.

Upload Files to Azure with ASP.NET Core Web API

Let’s start with the server-side project creation. We are going to create a new ASP.NET Core Web API project named UploadAzure.Server.

As soon as we create our new project, we are going to modify the appsettings.json file by adding a connection string section:

{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft": "Warning",
            "Microsoft.Hosting.Lifetime": "Information"
        }
    },
    "ConnectionStrings": {
        "AzureConnectionString": "DefaultEndpointsProtocol=https;AccountName=blazoruploadazure;AccountKey=S7/svnosTfuXxGIJ20fl6Y1N3YcdHefHH...;EndpointSuffix=core.windows.net"
    },
    "AllowedHosts": "*"
}

We can find this connection string in the Access keys menu that we already have opened:

Access keys connection string for Azure storage

Then, we have to install the Azure.Storage.Blob library. This library helps us working with the Azure Storage Blob service. Also, we should be aware that the Microsoft.Azure.Storage.Blob library is now deprecated and the Azure.Storage.Blob library is recommended for use:

Azure Storage Blob library to Upload Files to Azure

After the installation, we are going to create a new UploadController in the Controllers folder and modify it:

[Route("api/upload")]
[ApiController]
public class UploadController : ControllerBase
{
    private readonly string _azureConnectionString;

    public UploadController(IConfiguration configuration)
    {
        _azureConnectionString = configuration.GetConnectionString("AzureConnectionString");
    }
}

So, we extract the Azure connection string from the appsettings.json file and store it in the private variable.

After extracting the connection string from the appsettings.json file, we can add a new action that will upload the file to the Azure storage:

[HttpPost]
public async Task<IActionResult> Upload()
{
    try
    {
        var formCollection = await Request.ReadFormAsync();
        var file = formCollection.Files.First();

        if(file.Length > 0)
        {
            var container = new BlobContainerClient(_azureConnectionString, "upload-container");
            var createResponse = await container.CreateIfNotExistsAsync();
            if(createResponse != null && createResponse.GetRawResponse().Status == 201)
                await container.SetAccessPolicyAsync(Azure.Storage.Blobs.Models.PublicAccessType.Blob);

            var blob = container.GetBlobClient(file.FileName);
            await blob.DeleteIfExistsAsync(DeleteSnapshotsOption.IncludeSnapshots);
            using (var fileStream = file.OpenReadStream())
            {
                await blob.UploadAsync(fileStream, new BlobHttpHeaders { ContentType = file.ContentType });
            }

            return Ok(blob.Uri.ToString());
        }

        return BadRequest();
    }
    catch (Exception ex)
    {
        return StatusCode(500, $"Internal server error: {ex}");
    }
}

Code explanation

First, we extract the file from the form collection. If the file exists, we get a reference to a container with the upload-container name. For that, we have to use the BlobContainerClient class and provide the Azure connection string and the container name. After getting a reference, we use the CreateIfNotExistsAsync method to create our container if it doesn’t exist. This method returns a result of type Task<Response<BlobContainerInfo>>where we can find different information about the status, headers, content stream, etc. So, we check if the result is different than null and also, is the Status 201 (created). If this is true, this means we successfully created our container, and we set a public access type to Blob. With this access type, we allow public access only to blobs in this container, but not to the container itself.

After that, we create a new blob client with the GetBlobClient method. If the blob with the same name exists, we delete it including the snapshots to completely delete the blob. Then, we create a stream and use the UploadAsync method to upload the blob with the specific content type. Finally, we return the URI of the blob.

Of course, if the file doesn’t exist, we return a bad request.

Testing

Now, we can test our logic. Let’s start the API, open postman, and send a POST request with a prepared form-data body:

Upload Files to Azure - Postman Request

We can see that our request is successful with the URI in a response. Also, we can inspect our Azure storage and click the Containers option:

Created Azure Container

There is our created container. Once we click on it, we are going to find our file there:

Uploaded picture on the Azure storage

Excellent.

You can click on the file and click the Download link to verify that the file was uploaded.

Upload Files to Azure – Blazor WebAssembly Implementation

Before we create a Blazor WASM app, we are going to add CORS support to our Web API project.

So, let’s modify the ConfigureServices method in the Startup class:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(policy =>
    {
        policy.AddPolicy("CorsPolicy", opt => opt
        .AllowAnyOrigin()
        .AllowAnyHeader()
        .AllowAnyMethod());
    });

    services.AddControllers();
}

And also the Configure method:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseHttpsRedirection();
    app.UseCors("CorsPolicy");

    ...
}

As we mentioned at the beginning of this article, we already have an article related to file upload with Blazor WebAssembly and ASP.NET Core Web API. Following that article, we are going to create a similar client functionality here.

So, let’s create a new Blazor WebAssembly application (we are not going to select ASP.NET Core Hosted checkbox) and name it UploadAzure.Client.

After the creation, we are going to modify the launchSettings.json file:

{
  "profiles": {
    "UploadAzure.Client": {
      "commandName": "Project",
      "dotnetRunMessages": "true",
      "launchBrowser": true,
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
      "applicationUrl": "https://localhost:5011;http://localhost:5012",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

Here, we remove unnecessary parts of the configuration and change the ports for the client app.

After that, we have to modify the HttpClient registration in the Program.cs file:

public static async Task Main(string[] args)
{
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add<App>("#app");

    builder.Services.AddScoped(sp => new HttpClient
    {
        BaseAddress = new Uri("https://localhost:5001/api/")
    });

    await builder.Build().RunAsync();
}

Then, let’s create a new ImageUpload.razor file in the Shared folder:

<p>
    <InputFile OnChange="@HandleSelected" />

    @if (ImgUrl != null)
    {
        <div>
            <img src="@ImgUrl" class="image-preview" />
        </div>
    }
</p>

Here we use the InputFile component to enable file upload actions and also show the uploaded image once we get the URI from the server.

Of course, we need the ImageUpload.razor.cs file:

public partial class ImageUpload
{
    [Inject]
    public HttpClient HttpClient { get; set; }

    public string ImgUrl { get; set; }

    private async Task HandleSelected(InputFileChangeEventArgs e)
    {
        var imageFile = e.File;

        if (imageFile == null)
            return;

        var resizedFile = await imageFile.RequestImageFileAsync("image/png", 300, 500);

        using (var ms = resizedFile.OpenReadStream(resizedFile.Size))
        {
            var content = new MultipartFormDataContent();
            content.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data");
            content.Add(new StreamContent(ms, Convert.ToInt32(resizedFile.Size)), "image", imageFile.Name);

            var response = await HttpClient.PostAsync("upload", content);
            ImgUrl = await response.Content.ReadAsStringAsync();
        }
    }
}

Here, we read the file, create a stream, create a request payload, and send a request to the server. Once we get the result, we populate the ImgUrl property to show the image on the screen.

Finally, we have to call this component in the Index.razor file:

@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.

<ImageUpload />

Testing

That’s all it takes. We can now start our API and Blazor applications.

As soon as the Blazor app starts, we are going to see the Upload button on the screen.

We can click the button and select the image to upload:

Uploaded picture with Blazor to Azure storage

Also, we can check our container on the Azure storage to verify that the file is uploaded.

Of course, you can always refer to the mentioned article, to learn how to return the URL of the image to the parent component and how to store it in the database as a part of the created entity.

Conclusion

In this article, we have learned:

  • About creating an Azure Storage Account
  • How to prepare a server project for file upload action to Azure
  • The way to create a Blazor WebAssembly application to support the file upload action

In the next article, we are going to learn how to download files from this Azure blob storage.

So, until that one,

Best regards.