Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

358
Views
I'm streaming a file download in parts from a .NET 5 Controller. Curl sees the parts, why doesn't the browser?

Note: I've substantially rewritten this question because the original premise was flawed.

What I'm ultimately trying to achieve is to stream hundreds of thousands of records out of a database as a downloadable CSV file without spanking the server's memory, but I also want the browser to show the download of indeterminate size starting immediately and growing as more records are retrieved.

I've established that Kestrel is flushing content as I write it out (regardless of how I compose the output). I know this because when I request the file with curl I see it streaming into stdout one part at a time. However when I request the file in a browser (Chrome or Firefox) it doesn't show a downloading file until the last record has been emitted, which makes for a bad user experience because the download URL just appears to be broken. The following simple controller action demonstrates the problem.

What do I need to do to this code to make Chrome immediately pop up the download bar and show the file as downloading as soon as I've emitted the headers?

[ApiController]
    [Route("[controller]")]
    public class HomeController : ControllerBase {
        [HttpGet("[action]")]
        public async Task Test() {
            Response.StatusCode = 200;
            Response.ContentType = "text/plain";
            Response.ContentLength = null;
            Response.StatusCode = StatusCodes.Status200OK;
            Response.Headers.Add(HeaderNames.ContentDisposition, new ContentDispositionHeaderValue("attachment") { FileName = "test.txt" }.ToString());
            await Response.StartAsync();
            for (var i = 0; i < 5; ++i) {
                await Response.WriteAsync($"Iteration {i} at {DateTime.Now}\r\n");
                await Response.Body.FlushAsync();
                await Task.Delay(2000);
            }
        }
    }
over 4 years ago · Santiago Trujillo
1 answers
Answer question

0

Solved it. I changed the content type to application/octet-stream and now it behaves as I wanted it to.

Full working solution:

[ApiController]
    [Route("[controller]")]
    public class HomeController : ControllerBase {
        [HttpGet("[action]")]
        public async Task Test() {
            Response.StatusCode = 200;
            Response.ContentType = "application/octet-stream";
            Response.ContentLength = null;
            Response.StatusCode = StatusCodes.Status200OK;
            Response.Headers.Add(HeaderNames.ContentDisposition, new ContentDispositionHeaderValue("attachment") { FileName = "test.txt" }.ToString());
            await Response.StartAsync();
            for (var i = 0; i < 5; ++i) {
                await Response.WriteAsync($"Iteration {i} at {DateTime.Now}\r\n");
                await Response.Body.FlushAsync();
                await Task.Delay(2000);
            }
        }
    }
over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!