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.


2 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.

Leave a Reply

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