Uploading file from Blazor WebAssembly App directly to Blob storage

4.8k views Asked by At

I've been trying to develop a Blazor WebAssembly app (I'm trying with both .NET Standard 2.1 and .NET 5.0) and my goal is to allow the user to select a file using InputFileand for that file to be uploaded to an Azure Blob Storage Container. I've been looking around a lot and following different guides here and there but with no success. The issues I was obtaining were usually related to security for example CORS (Although it had been fully set up), Authorization fails and System.PlatformNotSupportedException: System.Security.Cryptography.Algorithms is not supported on this platform.

Regardless of whether it is good practice or not; is it possible to directly upload to a blob storage from a blazor app? One method i tried was via a SAS token. It works via a CONSOLE APP but not a BLAZOR APP.

<label for="browseData"><b>Browse File</b></label>
<p><InputFile id="browseData" OnChange="@OnInputFileChange" /></p>

private async Task OnInputFileChange(InputFileChangeEventArgs e)
{
    var maxAllowedFiles = 1;

    var inputFile = e.GetMultipleFiles(maxAllowedFiles).First();

    var stream = inputFile.OpenReadStream();

    await StorageService.UploadFileToStorage(stream, "sftp-server", inputFile.Name);
}

Storage Service

public class AzureStorageService

{
    private readonly IAzureStorageKeyService _azureStorageKeyService;

    public AzureStorageService(IAzureStorageKeyService azureStorageKeyService)
    {
        _azureStorageKeyService = azureStorageKeyService;
    }

    public async Task<Uri> UploadFileToStorage(Stream stream, string container, string fileName)
    {
        try
        {
            const string REPLACE_THIS_ACCOUNT = "test";

            var blobUri = new Uri("https://"
                                  + REPLACE_THIS_ACCOUNT +
                                  ".blob.core.windows.net/" +
                                  container + "/" + fileName);
            // Create the blob client.

            AzureSasCredential azureSasCredential = new AzureSasCredential(
                "?sv=2019-12-12&ss=bfqt&srt=sco&sp=rwdlacupx&se=2021-01-20T04:21:45Z&st=2021-01-19T20:21:45Z&spr=https&sig=OIkLePYDcF2AChtYUKs0VxUajs4KmwSyOXpQkFLvN2M%3D");

            var blobClient = new BlobClient(blobUri, azureSasCredential);

            // Upload the file
            var response = await blobClient.UploadAsync(stream, true);

            return blobUri;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            return null;
        }

    }
}

Like I was mentioning this will work via a console app but not a blazor app due to CORS..is this a security feature that just cannot be bypassed and just has to be done via the server side through a function -> blob?

2

There are 2 answers

0
Just the benno On BEST ANSWER

I was able to upload a file with your code. It was working like a charm. So, there is no error in your source code. I used the Microsoft.NET.Sdk.BlazorWebAssembly SDK with net5.0 as the target framework. Probably, it's a configuration issue with the storage account.

CORS policies

The CORS policy allows the host of the application (https://localhost:5001) to accesses the resources with any verb and any header (*) values. The response can include the content-length header. Is your WASM self hosted or hosted within an ASP core application? You can easily spot what origin is sent to azure by inspected the request.

what origin is used to send a request

Based on your context, only a few verbs like PUT might be enough.

The configuration of the SAS key can also be more specific.

Configuration for SAS

Using SAS from Blazor WASM

Even not entirely part of your questions, but worth mentioning. If you want to allow a direct upload into your blob storage - and there are good reasons for it - before uploading a file, your WASM app should send a request to your API, the response contains the SAS key that can only be used for this one operation. The SAS key itself should be a user delegation SAS key

0
Jim Xu On

If you want to directly upload file to Azure blob in Blazor WebAssembly application, we need to configure CORS fro Azure storage account. Regarding how to configure it, please refer to here.

For example

Configure CORS

Allowed origins: *
Allowed verbs: DELETE,GET,HEAD,MERGE,POST,OPTIONS,PUT
Allowed headers: *
Exposed headers: *
Maximum age (seconds): 3600

enter image description here

Create Account SAS. enter image description here

My upload file compoment

@page "/fileupload"
@using System.ComponentModel.DataAnnotations
@using System.IO
@using System.Linq
@using System.Threading
@using Azure.Storage.Blobs
@using Azure.Storage.Blobs.Models
@using Azure.Storage
@using Azure


<h3>File Upload Component</h3>
<label for="browseData"><b>Browse File</b></label>
<p><InputFile id="browseData" OnChange="@OnInputFileChange" /></p>
@{
    var progressCss = "progress " + (displayProgress ? "" : "d-none");
}

<div class="@progressCss">
    <div class="progress-bar" role="progressbar" style="@($"width: { progressBar }%")" aria-valuenow="@progressBar" aria-valuemin="0" aria-valuemax="100"></div>
</div>

@code {
    private bool displayProgress = false;
    private string result = string.Empty;
    private string progressBar;
    private int maxAllowedSize = 10 * 1024 * 1024;
    private async Task OnInputFileChange(InputFileChangeEventArgs e)
    {
        var maxAllowedFiles = 1;

        var inputFile = e.GetMultipleFiles(maxAllowedFiles).First();

        var blobUri = new Uri("https://"
                                  + "23storage23" +
                                  ".blob.core.windows.net/" +
                                  "upload" + "/" + inputFile.Name);


        AzureSasCredential credential = new AzureSasCredential("");
        BlobClient blobClient = new BlobClient(blobUri, credential, new BlobClientOptions());
        displayProgress = true;

        var res = await blobClient.UploadAsync(inputFile.OpenReadStream(maxAllowedSize), new BlobUploadOptions
        {
            HttpHeaders = new BlobHttpHeaders { ContentType = inputFile.ContentType },
            TransferOptions = new StorageTransferOptions
            {
                InitialTransferSize = 1024 * 1024,
                MaximumConcurrency = 10
            },
            ProgressHandler = new Progress<long>((progress) =>
            {
                progressBar = (100.0 * progress / inputFile.Size).ToString("0");

            })

        });





    }
}

enter image description here enter image description here

Besides, you also can download my sample to do a test.