FileStreamResult resulting in significantly more I/O than client is requesting
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
### Is there an existing issue for this?
- [X] I have searched the existing issues
### Describe the bug
I have troubles understanding why returning a `FileStreamResult` from my API is causing a large amount of bytes being read from the underlying `FileStream` without the client actually starting to read from the `HttpResponseMessage`stream.
I also observed different results running on Kestrel vs IIS Express.
Running on Kestrel the output is:
`Total bytes read: 524288`
Running on IIS Express the whole file is read:
`Total bytes read: 2498125`
Based on the client code below which does not actually read from the stream I would not expect that the server reads the whole file and writes it to the response body.
Is it possible to prevent this scenario and actually only write bytes to the response body which the client requested to read?
(calling `stream.ReadAsync(...)`)
Response buffering is not enabled and the default response buffer settings for Kestrel are lower than what I am seeing here.
The problem is that this is causing a lot of unnecessary disk I/O if clients are not going to read the stream or are aborting the request.
Client
```
HttpClient httpClient = new();
HttpResponseMessage response = await httpClient.GetAsync("https://localhost:44367/files/test", HttpCompletionOption.ResponseHeadersRead);
await using Stream stream = await response.Content.ReadAsStreamAsync();
await Task.Delay(1000);
```
Server
```
[ApiController]
[Route("[controller]")]
public class FilesController : ControllerBase
{
[HttpGet("test")]
public async Task Get()
{
FileStream fileStream = new LoggingFileStream("ForBiggerBlazes.mp4", FileMode.Open);
FileStreamResult fileStreamResult = new(fileStream, "video/mp4");
return fileStreamResult;
}
}
public class LoggingFileStream : FileStream
{
private int totalReadCount;
public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
int read = await base.ReadAsync(buffer, offset, count, cancellationToken);
totalReadCount += read;
Console.WriteLine("Total bytes read: " + totalReadCount);
return read;
}
}
```
```
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
WebApplication app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
```
### Expected Behavior
_No response_
### Steps To Reproduce
_No response_
### Exceptions (if any)
_No response_
### .NET Version
8.0.204
### Anything else?
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.