在我的 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
非常感谢任何对此的帮助。
请尝试使用
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));
}