通过 .NET FileStreamResult 和 Web 流式传输 S3 文件 API

Stream S3 file via .NET FileStreamResult and Web API

我有一个代码片段应该使用文件流下载 S3 文件:

public async Task<FileStreamResult> DownloadReportFileAsFileStream(String fileName)
{
    var request = new GetObjectRequest { BucketName = this.BucketName, Key = fileName };
    using var response = await this.S3Client.GetObjectAsync(request);
    var (mimeType, originalName) = response.Metadata.ExtractKnownMetaData();
    return new FileStreamResult(response.ResponseStream, mimeType) { FileDownloadName = originalName };
}

似乎是合法的,但这段代码以某种方式抛出异常。这是堆栈跟踪的顶部片段:

System.NotSupportedException:  The ReadAsync method cannot be called when another read operation is pending.
         at System.Net.Security.SslStream.ReadAsyncInternal[TIOAdapter](TIOAdapter adapter, Memory`1 buffer)
         at System.Net.Http.HttpConnection.ReadAsync(Memory`1 destination)

奇怪的是,如果我将 s3 响应流读入内存,一切正常(除了立即处理内存中的文件内容):

// FileStorage method to read the file data as a byte array.
public async Task<Byte[]> DownloadReportFileAsByteArray(String fileName)
{
    var request = new GetObjectRequest { BucketName = this.BucketName, Key = fileName };
    using var response = await this.S3Client.GetObjectAsync(request);
    await using var stream = response.ResponseStream;
    await using var memory = new MemoryStream();
    await stream.CopyToAsync(memory);
    return memory.ToArray();
}
...
// returns a FileContentResult object from the file byte array.
public async Task<FileContentResult> DownloadReportS3(DownloadReportRequest request)
{
    var bytes = await this.ReportFileStorage.DownloadReportFileAsByteArray(request.Id);
    var (mimeType, originalName) = await this.ReportFileStorage.GetReportFileMetaData(request.Id);
    return new FileContentResult(bytes, new MediaTypeHeaderValue(mimeType))
    {
        FileDownloadName = originalName
    };
}

流式传输方法无法正常工作是否有原因?解决方法将非常适用。

我不应该对响应对象使用 using 语句:

public async Task<FileStreamResult> DownloadReportFileAsFileStream(String fileName)
{
    var request = new GetObjectRequest { BucketName = this.BucketName, Key = fileName };
    var response = await this.S3Client.GetObjectAsync(request);
    var (mimeType, originalName) = response.Metadata.ExtractKnownMetaData();
    return new FileStreamResult(response.ResponseStream, mimeType) { FileDownloadName = originalName };
}

似乎 FileStreamResult 将在内部处理流处理。