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

Blazor WASM文件下载API报错:该流不支持超时

解决Azure Blob流式传输时的"Timeouts are not supported on this stream"错误

问题原因

当你在Minimal API中返回Results.Ok(Stream)时,ASP.NET Core会尝试将Stream对象序列化为JSON。但Azure Blob Storage的OpenReadAsync返回的Stream实现不支持ReadTimeout属性,Json序列化器在反射访问该属性时触发了InvalidOperationException。

修复方案

正确的做法是直接将Blob流写入HTTP响应体,而非让框架序列化Stream对象。同时需要设置必要的响应头,确保客户端能正确处理文件下载。

1. 扩展BlobService以返回元数据

修改BlobService,返回包含流、Content-Type和文件名的结果对象,方便API设置响应头:

public class BlobStreamResult
{
    public Stream Stream { get; init; }
    public string ContentType { get; init; }
    public string FileName { get; init; }
}

public class BlobService : IBlobService
{
    readonly BlobServiceClient _svcClient;
    BlobContainerClient GetContainerClient(string containerName) => _svcClient.GetBlobContainerClient(containerName);
    BlobClient GetBlobClient(string containerName, string file) => GetContainerClient(containerName).GetBlobClient(file);
    
    public BlobService(string blobEndpoint)
    {
        var cred = new DefaultAzureCredential();
        _svcClient = new BlobServiceClient(new Uri(blobEndpoint), cred);
    }

    public async Task<BlobStreamResult> GetBlobStreamWithMetadata(string containerName, string file)
    {
        var blobClient = GetBlobClient(containerName, file);
        var propertiesResponse = await blobClient.GetPropertiesAsync();
        var stream = await blobClient.OpenReadAsync();
        
        return new BlobStreamResult
        {
            Stream = stream,
            ContentType = propertiesResponse.Value.ContentType,
            FileName = file
        };
    }
}

2. 修改API端点直接流式传输

调整Minimal API端点代码,直接操作HttpContext.Response写入流并设置响应头:

app.MapGet("/download/{container}/{blob}", async (HttpContext ctx, IBlobService blobSvc, string container, string blob) =>
{
    var blobResult = await blobSvc.GetBlobStreamWithMetadata(container, blob);
    
    // 设置响应头,指定文件类型和下载行为
    ctx.Response.ContentType = blobResult.ContentType;
    ctx.Response.Headers.ContentDisposition = $"attachment; filename=\"{blobResult.FileName}\"";
    
    // 将Blob流复制到响应体,流式传输
    await using (blobResult.Stream)
    {
        await blobResult.Stream.CopyToAsync(ctx.Response.Body);
        await ctx.Response.Body.FlushAsync();
    }
    
    return Results.Ok();
})
.RequireAuthorization(AuthzPolicy.Policy);

3. 客户端代码无需修改

Blazor WASM的下载代码和JS互操作逻辑保持不变即可,PowerShell的iwr命令也能正确处理流式响应,不会再触发序列化错误。

关键说明

  • 直接流式传输避免了将整个Blob加载到内存,完全符合你降低API内存消耗的需求。
  • Content-Disposition头会让浏览器自动识别为下载文件,提升用户体验。
  • 使用await using确保Blob流被正确释放,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 10:45:03