此流不支持超时。无法从 API 接收流响应并将文件保存到 Blazor WASM 中的本地磁盘

问题描述 投票:0回答:1

在我的 Blazor WASM 应用程序中,我有文件下载功能。当调用我的 API 下载特定文件时 - API 调用保存文件的 Blob 存储并将其返回给客户端。

由于 blob 可能更大,我希望最大限度地减少 API 上的内存消耗,因此,我希望将 blob 作为流获取并将其传递给客户端。

我正在使用 Azure 存储 SDK 与 Blob 存储交互。我的 API 返回此错误

System.InvalidOperationException: Timeouts are not supported on this stream.
         at System.IO.Stream.get_ReadTimeout()
         at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.GetMemberAndWriteJson(Object obj, WriteStack& state, Utf8JsonWriter writer)
         at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryWrite(Utf8JsonWriter writer, T value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.TryWrite(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.JsonConverter`1.WriteCore(Utf8JsonWriter writer, T& value, JsonSerializerOptions options, WriteStack& state)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.SerializeAsync(Stream utf8Json, T rootValue, CancellationToken cancellationToken, Object rootValueBoxed)
         at Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.WriteAsJsonAsyncSlow[TValue](Stream body, TValue value, JsonSerializerOptions options, CancellationToken cancellationToken)
         at Microsoft.AspNetCore.Http.RequestDelegateFactory.ExecuteTaskResult[T](Task`1 task, HttpContext httpContext)
         at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
         at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)

这是代码

最小 API 端点

app.MapGet("/download/{container}/{blob}", async (HttpContext ctx, [FromServices] IBlobService blobSvc, string container, string blob) =>
{
    var b = await blobSvc.GetBlobStream(container, blob);
    return Results.Ok(b);
})
.RequireAuthorization(AuthzPolicy.Policy)
;

blob 服务/使用 Azure 存储 SDK

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 (new Uri (blobEndpoint), cred);
    }

    
    public async Task<Stream> GetBlobStream(string containerName, string file)
    {
        var blobClient = _getBlobClient(containerName, file);
        return await blobClient.OpenReadAsync();
    }
}

Blazor WASM 客户端代码下载

async Task _download(string container, string blob)
{
    HttpClient http = httpFactory.CreateClient(HttpClientName.REST);
    HttpResponseMessage resp = await http.GetAsync($"/download/{container}/{blob}");
    if (!resp.IsSuccessStatusCode)
    {
        throw new ApplicationException(JsonSerializer.Serialize(resp.ReasonPhrase));
    }

    // JS interop due to WASM
    using var streamRef = new Microsoft.JSInterop.DotNetStreamReference(await resp.Content.ReadAsStreamAsync());
    await _js.InvokeVoidAsync("download", blob, streamRef);
}

Javascript 互操作

async function download(fileName, fileStream) {
    const arrayBuffer = await fileStream.arrayBuffer();
    const blob = new Blob([arrayBuffer]);
    const url = URL.createObjectURL(blob);

    triggerDownload(fileName, url);
    URL.revokeObjectURL(url);
}

function triggerDownload(fielName, url) {
    const anchorElement = document.createElement("a");
    anchorElement.href = url;
    anchorElement.download = fielName;
    anchorElement.click();
    anchorElement.remove();
}

你发现这有什么问题吗?当我只是使用Powershell通过以下方式请求下载时,我得到了同样的错误,这让我认为它与客户端代码无关,而是错误在API层。

iwr https://localhost:7029/download/container/blob

非常感谢任何对此的帮助。

c# asp.net-core streaming blazor-webassembly
1个回答
0
投票

请尝试使用

Results.File
代替
Results.Ok

并更改您的

GetBlobStream
,如下所示。

public async Task<Stream> GetBlobStream(string containerName, string file)
{
    var blobClient = _getBlobClient(containerName, file);
    return await blobClient.OpenReadAsync(new BlobOpenReadOptions(false));
}
© www.soinside.com 2019 - 2024. All rights reserved.