Reputation: 7536
I've got a code snippet that is supposed to download an S3 file using file streaming:
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 };
}
Seems legit but somehow this code throws an exception. Here's the top fragment of the stack trace:
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)
What is strange that if I read an s3 response stream into a memory everything works fine (except handling file content in memory at once):
// 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
};
}
Is there a reason why the streaming approach doesn't work correctly? A workaround would be very appriciated.
Upvotes: 3
Views: 2263
Reputation: 7536
I should not use the using
statement for the response object:
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 };
}
Seems FileStreamResult
will handle disposing of the stream internally.
Upvotes: 4