Upload Files From Browser to Azure Cosmos Db Blob Container
Recently I am working on an internal OA organization for a modest company. One of their business organization requirement is for the users to upload attachments when creating a ticket. Azure Hulk Storage is platonic for this scenario. However, the attachments are sized from a few KB to hundreds of MB, so information technology is necessary to bear witness a progress bar when user upload the files. Let's run across how tin we easily do it with Azure.
The Pattern
The OA system is using ASP.NET Cadre equally it'due south backend API. And so basically I tin come up with 2 designs for uploading files to Azure Blob Storage.
- Create an API that take files from web front end-end and so upload to Azure.
- Create an API return just SAS token for Azure Storage, web forepart-stop then utilize this token to upload files directly to Azure without calling our own API.
Selection 1 is as well expensive because this require API server to allow large request body, and I have to write a lot of complicated code to enable reporting progress to client, not to mention all the error scenarios.
Since Azure Storage already got JavaScript API that can report upload progress, I decided to go with option 2.
What need to be prepared in Azure
Container
Create a container for storing uploaded files. e.g. "attachments".
Connection String
Re-create the connexion cord from the storage account for afterwards apply.
CORS
Let CORS on Hulk service for your domain. For demo, I just enable all origins and all methods. Delight do not do this in product. To upload files, but HTTP PUT is needed.
Create API to return SAS Token
Install latest Azure.Storage.Blobs package.
<PackageReference Include="Azure.Storage.Blobs" Version="12.nine.1" /> Add together Azure Storage settings to appsettings.json
"AppSettings": { "AzureStorage": { "ConnectionString": "<connection string>", "ContainerName": "attachments", "TokenExpirationMinutes": 20 } } Delight adapt TokenExpirationMinutes as you need.
Create controller and action
[ApiController] [Road("[controller]")] public grade AttachmentController : ControllerBase { private readonly IConfiguration _configuration; public AttachmentController(IConfiguration configuration) { _configuration = configuration; } [HttpGet("token")] [ProducesResponseType(StatusCodes.Status409Conflict)] [ProducesResponseType(typeof(AzureStorageSASResult), StatusCodes.Status200OK)] public IActionResult SASToken() { var azureStorageConfig = _configuration.GetSection("AppSettings:AzureStorage").Get<AzureStorageConfig>(); BlobContainerClient container = new(azureStorageConfig.ConnectionString, azureStorageConfig.ContainerName); if (!container.CanGenerateSasUri) return Conflict("The container can't generate SAS URI"); var sasBuilder = new BlobSasBuilder { BlobContainerName = container.Name, Resource = "c", ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(azureStorageConfig.TokenExpirationMinutes) }; sasBuilder.SetPermissions(BlobContainerSasPermissions.All); var sasUri = container.GenerateSasUri(sasBuilder); var result = new AzureStorageSASResult { AccountName = container.AccountName, AccountUrl = $"{container.Uri.Scheme}://{container.Uri.Host}", ContainerName = container.Name, ContainerUri = container.Uri, SASUri = sasUri, SASToken = sasUri.Query.TrimStart('?'), SASPermission = sasBuilder.Permissions, SASExpire = sasBuilder.ExpiresOn }; render Ok(result); } } public course AzureStorageConfig { public string ConnectionString { go; prepare; } public cord ContainerName { get; set; } public int TokenExpirationMinutes { become; fix; } } public class AzureStorageSASResult { public string AccountName { become; prepare; } public string AccountUrl { go; set; } public Uri ContainerUri { get; prepare; } public string ContainerName { get; set; } public Uri SASUri { become; set up; } public string SASToken { get; prepare; } public cord SASPermission { become; set up; } public DateTimeOffset SASExpire { get; set; } } Notice the SAS Permissions are set to All for demo hither, in production, delight set to only the permissions y'all demand for security reasons.
sasBuilder.SetPermissions(BlobContainerSasPermissions.All); A correct API response volition await like this
{ "accountName": "cinderellastorage", "accountUrl": "https://cinderellastorage.blob.core.windows.internet", "containerUri": "https://cinderellastorage.blob.core.windows.internet/attachments", "containerName": "attachments", "sasUri": "https://cinderellastorage.blob.core.windows.net/attachments?sv=2020-08-04&se=2021-07-07T05%3A37%3A07Z&sr=c&sp=racwdxlt&sig=MxCEEtT%2FGVbjksDzgzVgvRqucQfjKd4JOp6zsId0h5c%3D", "sasToken": "sv=2020-08-04&se=2021-07-07T05%3A37%3A07Z&sr=c&sp=racwdxlt&sig=MxCEEtT%2FGVbjksDzgzVgvRqucQfjKd4JOp6zsId0h5c%3D", "sasPermission": "racwdxlt", "sasExpire": "2021-07-07T05:37:07.6443444+00:00" } Web Front-End
I am not a pro front-end programmer. And so I used code from this post with a petty modification. The azure-storage.blob.min.js library can be downloaded from https://aka.ms/downloadazurestoragejs
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta proper name="viewport" content="width=device-width, initial-calibration=1.0"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.ii/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <championship>Upload</title> <script src="azure-storage.blob.min.js"></script> <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.six.0.min.js"></script> <script type="text/javascript"> $(document).on('change', ':file', function () { var input = $(this) var label = $('#BrowseInput').val(input.val().supplant(/\\/one thousand, '/').supplant(/.*\//, '')); }); </script> <script type="text/javascript"> part displayProcess(process) { document.getElementById("uploadProgressBar").style.width = process + '%'; certificate.getElementById("uploadProgressBar").innerHTML = process + '%'; } role uploadBlob() { displayProcess(0); certificate.getElementById("uploadProgressBarContainer").classList.remove('hidden'); // TODO: Telephone call API to get URL, SAS Token, Container proper noun var blobUri = 'https://cinderellastorage.blob.core.windows.cyberspace'; var containerName = 'attachments'; var sasToken = 'sv=2020-08-04&se=2021-07-07T05%3A40%3A03Z&sr=c&sp=racwdxlt&sig=NKDKAdQESM03GxCfFs2FLHHYLWJzqRYy4LKdyxTcVx8%3D'; var blobService = AzureStorage.Blob.createBlobServiceWithSas(blobUri, sasToken); var file = $('#FileInput').get(0).files[0]; var customBlockSize = file.size > 1024 * 1024 * 32 ? 1024 * 1024 * four : 1024 * 512; blobService.singleBlobPutThresholdInBytes = customBlockSize; var finishedOrError = faux; var speedSummary = blobService.createBlockBlobFromBrowserFile(containerName, file.name, file, { blockSize: customBlockSize }, function (error, result, response) { finishedOrError = true; if (error) { alert('Fault'); } else { displayProcess(100); } }); function refreshProgress() { setTimeout(function () { if (!finishedOrError) { var process = speedSummary.getCompletePercent(); displayProcess(process); refreshProgress(); } }, 200); } refreshProgress(); } </script> </caput> <torso> <div class="modal-dialog"> <div form="modal-content"> <class asp-controller="Abode" asp-action="UploadSmallFile" enctype="multipart/form-data" id="BlobUploadForm" method="post" class="form-label-left" office="form"> <div grade="modal-body"> <div form="form-group"> <div class="input-group"> <label class="input-grouping-btn"> <span course="btn btn-master"> Browse… <input blazon="file" manner="display: none;" name="file" id="FileInput"> </span> </label> <input type="text" class="grade-control" readonly="" id="BrowseInput"> </div> </div> <div class="form-grouping"> <div form="input-group"> <button type="button" value="Upload to Blob" form="btn btn-success" id="UploadBlob" onclick="uploadBlob()">Upload to Blob</push> </div> </div> <div class="course-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> <script src="https://cdn.jsdelivr.internet/npm/bootstrap@five.0.two/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="bearding"></script> </body> </html> The bones idea in this code is to break large files to trunks and upload them piece by slice. Azure storage SDK volition handle the upload and report the progress.
Finally, I can verify the file has been uploaded to Azure Storage.
Source: https://edi.wang/post/2021/7/7/upload-file-with-progress-from-browser-to-azure-blob-storage
0 Response to "Upload Files From Browser to Azure Cosmos Db Blob Container"
Post a Comment