Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

345
Vistas
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 Respuestas
Responde la pregunta

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 Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda