Azure与ASP.NET Core 6 MVC:Blob存储图片高效获取方案咨询
无需SAS密钥,通过已认证用户直接用img标签访问Azure Blob存储图片
由于img标签无法自定义HTTP请求头,直接让前端携带Azure AD令牌访问Blob存储是不可行的。以下是两种符合你需求的解决方案,无需使用存储账户密钥生成的SAS:
方案1:使用用户委托SAS(User Delegation SAS)
这种SAS基于已认证用户的Azure AD身份生成,不依赖存储账户密钥,仅授权当前用户在有效期内访问指定Blob。
实现步骤:
配置Azure AD权限:
- 在Azure门户中找到目标Blob容器,进入「访问控制(IAM)」页面。
- 添加「存储Blob数据读取者」角色分配,选择已认证用户所属的AD组或单个用户,确保用户具备生成用户委托SAS的权限。
后端生成SAS URL接口:
注入BlobServiceClient(建议通过配置文件读取连接字符串,而非硬编码),编写接口根据用户身份生成指定图片的SAS URL:private readonly BlobServiceClient _blobServiceClient; private readonly string _blobContainerName = "your-container-name"; public ImageController(BlobServiceClient blobServiceClient) { _blobServiceClient = blobServiceClient; } [Authorize] [HttpPost("get-sas-urls")] public async Task<IActionResult> GetSasUrls([FromBody] List<string> imageNames) { // 获取当前用户的Azure AD访问令牌 var accessToken = await HttpContext.GetTokenAsync("access_token"); // 获取用户委托密钥,有效期1小时 var userDelegationKey = await _blobServiceClient.GetUserDelegationKeyAsync( DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddHours(1)); var containerClient = _blobServiceClient.GetBlobContainerClient(_blobContainerName); var sasUrls = new List<string>(); foreach (var imageName in imageNames) { var blobClient = containerClient.GetBlobClient(imageName); if (!await blobClient.ExistsAsync()) continue; var sasBuilder = new BlobSasBuilder { BlobContainerName = _blobContainerName, BlobName = imageName, Resource = "b", // 指定资源为Blob ExpiresOn = DateTimeOffset.UtcNow.AddHours(1) }; sasBuilder.SetPermissions(BlobSasPermissions.Read); // 生成用户委托SAS令牌 var sasToken = sasBuilder.ToSasQueryParameters(userDelegationKey, _blobServiceClient.AccountName).ToString(); sasUrls.Add($"{blobClient.Uri}?{sasToken}"); } return Json(sasUrls); }前端调用渲染:
通过Ajax调用上述接口拿到SAS URL列表,直接将img标签的src指向对应URL即可。
方案2:后端代理图片请求
通过后端接口代理图片访问,验证用户身份后直接流式返回Blob内容,完全无需SAS。
实现步骤:
配置应用服务托管标识权限:
- 为你的Azure App Service启用系统托管标识。
- 在Blob容器的「访问控制(IAM)」中,添加「存储Blob数据读取者」角色分配,选择该应用服务的托管标识。
后端代理接口:
编写接口验证用户权限后,流式返回Blob内容:[Authorize] [HttpGet("images/{imageName}")] public async Task<IActionResult> GetImage(string imageName) { // 校验当前用户是否有权访问该图片所属的图库(根据你的业务逻辑实现) var hasAccess = await VerifyUserGalleryAccess(User, imageName); if (!hasAccess) { return Forbid(); } var containerClient = _blobServiceClient.GetBlobContainerClient(_blobContainerName); var blobClient = containerClient.GetBlobClient(imageName); if (!await blobClient.ExistsAsync()) { return NotFound(); } // 流式返回Blob内容,避免加载到内存 var stream = await blobClient.OpenReadAsync(); // 根据图片实际类型调整MIME类型(如image/png、image/gif等) return File(stream, "image/jpeg"); }前端渲染:
直接将img标签的src指向代理接口,例如:<img src="/api/images/your-image-name.jpg" alt="Gallery image">
现有代码优化建议
你当前的GetImagesFromBlob函数存在几个问题,建议调整:
- 避免硬编码连接字符串,通过
IConfiguration读取并注入BlobServiceClient。 - 异步方法需用
await调用,避免同步阻塞线程,将函数改为异步。 - 无需遍历容器内所有Blob,直接根据数据库返回的图片名称定位对应Blob,提升效率。
内容的提问来源于stack exchange,提问作者Zdenek Barta
相关产品推荐
相关产品推荐

