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.


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

Leave a Reply

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