You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在.NET中代理Azure Blob存储请求且不占用全量内存?

问题:Azure Blob存储图片代理优化(避免内存缓存)

我有一个支持用户上传图片的应用,图片存储于Azure Blob Storage,理论上可通过直接URL访问。但我不想向已通过Entra ID凭证登录的用户暴露Blob存储地址,同时希望避免为每个页面的每张图片生成SAS令牌的繁琐操作。

能否在应用中代理对Azure存储的请求,无需将整个对象保存在内存中就返回结果?

具体流程如下:

  • 用户调用https://myapp.com/attachment/image001.jpg
  • 控制器接收请求并验证用户对该端点的权限(authz)
  • 控制器接着调用https://myappsstorage.blob.core.windows.net/appattachmentcontainer/image001.jpg
  • 控制器无需分配内存保存完整下载图片,直接将结果转发给原始调用者。

我之前实现过类似功能,但都是先下载完整结果并保存在内存中再返回。我想优化这一实现,却不清楚.NET中的具体机制。

示例代码

public async Task<IActionResult> Attachment()
{

    using var httpClient = new HttpClient();

    // 注:这会分配整个流的内存,对吗?
    var downloadedImage = await httpClient.GetStreamAsync(
        "https://127.0.0.1:10000/devstoreaccount1/magic-scaler/04108_lakeohau_3840x2400.jpg");
    return File(downloadedImage, "image/jpg", DateTimeOffset.UtcNow, EntityTagHeaderValue.Any);
}

解决方案

可以实现,核心思路是利用流式传输,让Azure Blob的响应流直接通过控制器转发给客户端,全程不把完整文件加载到内存。

优化后的实现方案

在ASP.NET Core中,你可以通过以下两种方式实现:

方式1:使用Azure.Storage.Blobs SDK直接获取Blob流并转发

推荐使用官方SDK而非HttpClient,因为SDK更适配Blob存储的特性,且能直接获取可流式传输的响应:

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Specialized;

public async Task<IActionResult> GetAttachment(string fileName)
{
    // 1. 先做权限验证(根据你的业务逻辑实现)
    if (!UserHasPermission(fileName))
    {
        return Forbid();
    }

    // 2. 初始化Blob客户端(建议将BlobServiceClient注册为单例)
    var blobServiceClient = new BlobServiceClient("你的存储连接字符串");
    var blobClient = blobServiceClient.GetBlobClient("appattachmentcontainer", fileName);

    // 3. 检查Blob是否存在
    if (!await blobClient.ExistsAsync())
    {
        return NotFound();
    }

    // 4. 获取Blob的流式响应,直接转发给客户端
    var response = await blobClient.DownloadAsync();
    
    // 设置响应头,保留Blob的原始ContentType和ETag等信息
    return File(response.Value.Content, response.Value.ContentType, response.Value.LastModified, response.Value.ETag);
}

方式2:使用HttpClient的流式响应优化

如果坚持用HttpClient,需要确保不将流全部读取到内存,而是直接传递响应流:

public async Task<IActionResult> Attachment(string fileName)
{
    // 权限验证
    if (!UserHasPermission(fileName))
    {
        return Forbid();
    }

    using var httpClient = new HttpClient();
    var blobUrl = $"https://myappsstorage.blob.core.windows.net/appattachmentcontainer/{fileName}";
    
    // 发送请求并获取响应消息(不直接读取流)
    var response = await httpClient.GetAsync(blobUrl, HttpCompletionOption.ResponseHeadersRead);
    
    if (!response.IsSuccessStatusCode)
    {
        return StatusCode((int)response.StatusCode);
    }

    // 获取响应流,直接返回给客户端
    var stream = await response.Content.ReadAsStreamAsync();
    
    // 从响应头中获取原始ContentType
    var contentType = response.Content.Headers.ContentType?.MediaType ?? "image/jpg";
    var lastModified = response.Content.Headers.LastModified;
    var etag = new EntityTagHeaderValue(response.Content.Headers.ETag?.Tag);

    return File(stream, contentType, lastModified, etag);
}

关键优化点说明

  • HttpCompletionOption.ResponseHeadersRead:使用这个选项时,HttpClient会在获取到响应头后就返回,而不是等待整个响应体下载完成,确保流是实时传输的。
  • 直接返回流:File方法接收流参数时,ASP.NET Core会自动处理流式传输,不会将整个流加载到内存,而是分块发送给客户端。
  • 使用官方SDK:Azure.Storage.Blobs的DownloadAsync方法返回的流是直接对接Blob存储的响应流,性能比HttpClient更优,且能方便获取Blob的元数据(如ContentType、ETag、LastModified)。

额外建议

  • 将BlobServiceClient或HttpClient注册为DI容器中的单例,避免每次请求创建新实例,提升性能。
  • 可以添加缓存逻辑(如基于ETag的缓存),减少重复请求Blob存储的次数。

内容的提问来源于stack exchange,提问作者Killnine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 02:37:44