Deprecated: Function create_function() is deprecated in D:\home\site\wwwroot\wp-content\plugins\wp-spamshield\wp-spamshield.php on line 2033

Uploading Files to Azure Blob Storage from the Browser

As part of an ongoing project I’ve been trying to upload files from from the browser to an ASP.NET Core MVC site, however there appears to be a hard limit preventing uploading files greater than ~28.6 MiB in size which I can’t manage to figure out a way around.

As one of the eventual destinations for these files was to be as a blob in Azure Storage there’s happily a set of JavaScript libraries from Microsoft (though these are still in preview) designed to interact with Azure Storage objects which I can use for this and which aren’t affected by size limits.

Azure Storage Account

The first step is to create a container in your storage account (as detailed here). Once the container has been created you need to create a CORS rule in it to allow the JavaScript running in the browser to access it. For testing purposes I set everything to “*” but this should be locked down when going live.

JavaScript Libraries

The GitHub project that contains the Azure Storage JavaScript library is here, in order to use it in your project you’ll need to download the client libraries from here and include the relevant ones in your project.

Web Application

The code for my test web app is here with the processing taken care of by the JavaScript the controller methods are unnecessary and so the below in Index.cshtml is all that’s required. It’s based on the example here.


@{
    ViewData["Title"] = "Home Page";
}

<div class="modal-dialog">
    <div class="modal-content">
        <form asp-controller="Home" asp-action="UploadSmallFile" enctype="multipart/form-data" id="BlobUploadForm" method="post" class="form-label-left" role="form">
            <div class="modal-body">
                <div class="form-group">
                    <div class="input-group">
                        <label class="input-group-btn">
                            <span class="btn btn-primary">
                                Browse… <input type="file" style="display: none;" name="file" id="FileInput">
                            </span>
                        </label>
                        <input type="text" class="form-control" readonly="" id="BrowseInput">
                    </div>
                </div>
                <div class="form-group">
                    <div class="input-group">
                        <button type="button" value="Upload to Blob" class="btn btn-default" id="UploadBlob" onclick="uploadBlob()">Upload to Blob</button>
                    </div>
                </div>
                <div class="form-group hidden" id="uploadProgressBarContainer">
                    Uploading...
                    <div class="progress">
                        <div class="progress-bar" role="progressbar" id="uploadProgressBar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 0%;">
                            0%
                        </div>
                    </div>
                </div>
            </div>
        </form>
    </div>
</div>

@section Scripts {
    <script src="~/vendor/azure-storage/azure-storage.blob.js"></script>
    <script type="text/javascript">
        $(document).on('change', ':file', function () {
            var input = $(this)
            var label = $('#BrowseInput').val(input.val().replace(/\\/g, '/').replace(/.*\//, ''));
        });
    </script>
    <script type="text/javascript">
        function displayProcess(process) {
            document.getElementById("uploadProgressBar").style.width = process + '%';
            document.getElementById("uploadProgressBar").innerHTML = process + '%';
        }

        function uploadBlob() {
            // Show progress bar
            displayProcess(0);
            document.getElementById("uploadProgressBarContainer").classList.remove('hidden');

            var blobUri = 'https://' + 'mycontainer' + '.blob.core.windows.net';
            var blobService = AzureStorage.Blob.createBlobServiceWithSas(blobUri, 'SAS_TOKEN');

            // If one file has been selected in the HTML file input element
            var file = $('#FileInput').get(0).files[0];

            var customBlockSize = file.size > 1024 * 1024 * 32 ? 1024 * 1024 * 4 : 1024 * 512;
            blobService.singleBlobPutThresholdInBytes = customBlockSize;

            var finishedOrError = false;
            var speedSummary = blobService.createBlockBlobFromBrowserFile('internal', file.name, file, { blockSize: customBlockSize }, function (error, result, response) {
                finishedOrError = true;
                if (error) {
                    alert('Error');
                } else {
                    displayProcess(100);
                }
            });

            function refreshProgress() {
                setTimeout(function () {
                    if (!finishedOrError) {
                        var process = speedSummary.getCompletePercent();
                        displayProcess(process);
                        refreshProgress();
                    }
                }, 200);
            }

            refreshProgress();
        }
    </script>
}

The SAS token for this was generated by an Azure Function I created which creates short-lived SAS tokens, an easier option might be to just create a long-lived SAS token in Storage Explorer, store it in the site appsettings.json and access it from there.


8 Comments

Vaira · 13th April 2018 at 1:00 pm

Uncaught ReferenceError: speedSummary is not defined error for the command speedSummary.getCompletePercent().

Any idea ?

    Shinigami · 13th April 2018 at 5:45 pm

    Hi Vaira, it’s hard to tell without seeing your code and my sample GitHub project linked above still seems to work for me.

    It’s possible you’re declaring the speedSummary variable after the refreshProgress function which could give that error, if that’s not the case feel free to send me a link to your code and I’ll have a quick look over it to see if I can spot anything obvious.

Mike S · 25th October 2018 at 10:44 am

Do you have any plans to make this handle multiple files, and include a progress bar or progress % status text display?

    Shinigami · 25th October 2018 at 11:28 am

    Hi Mike,

    There is a progress bar, at least for the blob upload. I haven’t got any plans to allow it to handle multiple files as this wasn’t necessary for the project I was working on, I’m not sure exactly how the file input works with multiple files but I’d imagine that it provides an array of selected files and you can then loop through these in the uploadBlob Javascript method.

Andrew C · 8th February 2019 at 11:52 pm

This didn’t work for me (Error box popped up). I am pretty sure I have not provided the correct credentials. Could you elaborate a little on the SAS token? I pasted my very long SAS token copied from the portal blob shared access signature section into the call to createBlobServiceWithSas, replacing ‘SAS_TOKEN’ in the above code. Is this what is needed?

I was able to make the “https://github.com/Azure-Samples/storage-blobs-node-quickstart/” sample work, so I think I have the storage account set correctly.

Any help appreciated. If I can get this to work it will exactly solve a customer problem I’m having.

a.

    Shinigami · 9th February 2019 at 7:50 am

    Hi, The SAS token can be obtained from Azure Storage Explorer by right clicking on the container and selecting create SAS key. The token is the URL parameters on the end which looks like the one found here.

    https://docs.microsoft.com/en-us/azure/storage/common/storage-dotnet-shared-access-signature-part-1#how-a-shared-access-signature-works

    One thing to note is that you obviously don’t want to be embedding long lived access tokens to your storage container in a webpage as these will be visible to everyone, this is why I used the Azure function mentioned above to create short lived tokens.

    A better approach depending on your circumstances is possibly to upload the file to the controller and to then send it to Azure storage from server side so that no credentials are exposed. I’ve got an example of this below though I didn’t manage to get it working for large files unfortunately.

    https://blog.bitscry.com/2018/03/12/uploading-files-in-asp-net-core-from-an-mvc-view/

      Andrew C · 9th February 2019 at 3:26 pm

      Thanks. This is very helpful and I appreciate the security suggesstions too.
      a.

      Andrew C · 11th February 2019 at 8:36 pm

      This works for me now. Thank you very much.
      I needed to change:

      “`var blobUri = ‘https://’ + ‘mycontainer’ + ‘.blob.core.windows.net’;“`
      to
      “` var blobUri = ‘https://’ + ‘mystorageaccount’ + ‘.blob.core.windows.net’ + ‘/mycontainer’;“

      Not sure why… but this was clear when I got the URL and key from storage explorer rather than the portal.
      a.

Leave a Reply

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