点击上方蓝字 / 关注“汪宇杰博客”
Azure Blob Storage
最近正在做一个小企业的办公系统,其中有个附件上传和下载的需求,非常适合使用 Azure Blob Storage 做存储。然而,附件的大小从几 KB 到数百 MB 不等,因此在用户上传文件时需要显示进度条。让我们看看如何套壳 Azure 轻松自主研发成功。
图 | 网络
EDIWANG
Azure Blob Storage
OA 系统使用 ASP.NET Core 作为后端 API。所以基本上我可以想出两种将文件上传到 Azure Blob 存储的设计。
方案1
创建一个 API,该 API 接受来自 Web 前端的文件,然后上传到 Azure。
方案2
为 Azure 存储创建一个只返回 SAS 令牌的 API,Web 前端然后使用此令牌将文件直接上传到 Azure,而无需调用我们自己的 API。
图 | 网络
而 Azure 存储已经拥有可以报告上传进度的 JavaScript API,因此我决定采用方案2。
EDIWANG
Azure Blob Storage
Container
创建一个容器来存储上传的文件。例如 “attachments”。
连接字符串
从存储帐户复制连接字符串以备后用。
CORS
在您的域的 Blob 服务上允许 CORS。对于演示,我允许所有来源和所有方法。请不要在生产环境中这样做,不然容易被程序员删库跑路报复社会。要上传文件,只需要 HTTP PUT 即可。
EDIWANG
创建API以返回SAS Token
Azure Blob Storage
以 ASP.NET Core 为例,安装最新版的 Azure.Storage.Blobs 包。
<PackageReference Include="Azure.Storage.Blobs" Version="12.9.1" />
将 Azure 存储账户设置写入 appsettings.json
"AppSettings": {
"AzureStorage": {
"ConnectionString": "<connection string>",
"ContainerName": "attachments",
"TokenExpirationMinutes": 20
}
}
可以根据自己需要调整 TokenExpirationMinutes 的值,它代表token过期时间。
创建 Controller 及 Action。
[ApiController]
[Route("[controller]")]
public class 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
};
return Ok(result);
}
}
public class AzureStorageConfig
{
public string ConnectionString { get; set; }
public string ContainerName { get; set; }
public int TokenExpirationMinutes { get; set; }
}
public class AzureStorageSASResult
{
public string AccountName { get; set; }
public string AccountUrl { get; set; }
public Uri ContainerUri { get; set; }
public string ContainerName { get; set; }
public Uri SASUri { get; set; }
public string SASToken { get; set; }
public string SASPermission { get; set; }
public DateTimeOffset SASExpire { get; set; }
}
注意这里的 SAS 权限设置为All,用于演示,在生产中,出于安全原因,请仅设置您需要的权限。对于文件上传,只需要 Create,Add,Write即可。
sasBuilder.SetPermissions(BlobContainerSasPermissions.All);
API 返回的结果如下:
{
"accountName": "cinderellastorage",
"accountUrl": "https://cinderellastorage.blob.core.windows.net",
"containerUri": "https://cinderellastorage.blob.core.windows.net/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"
}
EDIWANG
Web前端
Azure Blob Storage
我不是专业的前端开发人员。所以我自主研发了如下文章中的代码并稍作修改。azure-storage.blob.min.js 库可以从 https://aka.ms/downloadazurestoragejs 下载。
链接:https://blog.bitscry.com/2018/03/14/uploading-files-to-azure-blob-storage-from-the-browser/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<title>Upload</title>
<script src="azure-storage.blob.min.js"></script>
<script src="https://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211216_897eba5e-5e65-11ec-b838-fa163eb4f6be.png"></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() {
displayProcess(0);
document.getElementById("uploadProgressBarContainer").classList.remove('hidden');
// TODO: Call API to get URL, SAS Token, Container name
var blobUri = 'https://cinderellastorage.blob.core.windows.net';
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 * 4 : 1024 * 512;
blobService.singleBlobPutThresholdInBytes = customBlockSize;
var finishedOrError = false;
var speedSummary = blobService.createBlockBlobFromBrowserFile(containerName, 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>
</head>
<body>
<div>
<div>
<form asp-controller="Home" asp-action="UploadSmallFile" enctype="multipart/form-data" id="BlobUploadForm"
method="post" role="form">
<div>
<div>
<div>
<label>
<span class="btn btn-primary">
Browse… <input type="file" style="display: none;" name="file" id="FileInput">
</span>
</label>
<input type="text" readonly="" id="BrowseInput">
</div>
</div>
<div>
<div>
<button type="button" value="Upload to Blob" class="btn btn-success" id="UploadBlob"
onclick="uploadBlob()">Upload to Blob</button>
</div>
</div>
<div class="form-group hidden" id="uploadProgressBarContainer">
Uploading...
<div>
<div 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://oss-emcsprod-public.modb.pro/wechatSpider/modb_20211216_8af4c608-5e65-11ec-b838-fa163eb4f6be.png"
integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM"
crossorigin="anonymous"></script>
</body>
</html>
这段代码的基本思想是将大文件拆分成一个个小块,逐个上传。Azure 存储 SDK 将处理上传并报告进度。
最后,我可以验证文件是否已上传到 Azure 存储。
就这么轻松搞定了!用了Azure,丝毫没有996的机会。
图 | 网络
Azure | .NET | 微软 MVP
无广告,不卖课,做纯粹的技术公众号