1

I want to download a large csv file (100+ mb) through browser but I get timeout. Generating the csv file is time-consuming so I cannot read it all in memory or write to disk first.

public IActionResult Download1()
{
    var bytes = FetchBytes();
    return File(bytes, "text/csv", "download1.csv");
}

FileStreamResult requires pulling all the stream in memory.

// GET or POST
public IActionResult Download()
{
    var stream = FetchStream();
    return new FileStreamResult(stream, "text/csv");
}

I tried writing chunks to response and I can see the file download progress in browser but I do not get the file download or the file download dialog.

// GET or POST
public ActionResult Download()
{
    var response = this.Response;
    var size = 4096;
    foreach (var csvBatch in csvBatches)
    {
        byte[] bytes = Encoding.ASCII.GetBytes(csvBatch);
        using (var ms = new MemoryStream(bytes))
        {
            var data = new byte[size];
            var bytesRead = 0;
            do
            {
                bytesRead = ms.Read(data, 0, size);
                response.Body.Write(data, 0, readBytes);
                response.Body.Flush();
            } while (bytesRead > 0);
        }
    }
    response.ContentType = "text/csv";
    response.Headers.Add("Content-Disposition", $"attachment;filename=\"{fileName}\"");
    // this streams the file but does not download or give file download dialog
}

Net Core does not have the WriteFile(HttpResponseBase) method to override for FileResult as per Writing to Output Stream from Action which can write to Response.OutputStream.

How can I do this?

hIpPy
  • 4,649
  • 6
  • 51
  • 65
  • Does setting the response `ContentType` in your streaming example allow the download dialog to be displayed? – SpruceMoose Feb 01 '18 at 11:25
  • This might be worth a look: https://blog.stephencleary.com/2016/11/streaming-zip-on-aspnet-core.html – SpruceMoose Feb 01 '18 at 13:24
  • `FileStreamResult` should not pull the whole stream into memory on its own. If that's occurring, it's likely the fault of whatever `FetchStream` is doing. – Chris Pratt Feb 01 '18 at 14:12
  • @CalC, it does not. Updated question. – hIpPy Feb 01 '18 at 17:58
  • @chris-pratt: Do you have an example with FileStreamResult? The csvBatches code will not change and will return a stream instead. – hIpPy Feb 01 '18 at 17:58
  • Your main issue is that your CSV is *already* in-memory before it ever gets written to the response. There's nothing `FileStreamResult` can do about that. The only way to truly stream the CSV to the response without loading everything into memory first is to have your CSV writer directly write to the output stream (`Response.Body`). – Chris Pratt Feb 01 '18 at 18:38

1 Answers1

1

There are number of ways you can do it and also there are number of helpful classes available to use. Don't break your head, because they do chunk by default.

The one I would recommend is StreamContent class. It takes buffer size on the constructor.

Here is the sample how it works.

    public async Task<HttpResponseMessage> Download()
    {
        var response = new HttpResponseMessage(HttpStatusCode.OK);

        var stream = await GetStreamFromSomewhereMethod();

        var content = new StreamContent(stream, 4096); //buffer is 4KB

        response.Content = content;
        return response;
    }

Hope it helps.

Cinchoo
  • 6,088
  • 2
  • 19
  • 34
  • `Request.CreateResponse(HttpStatusCode.OK)` says `Request does not have a definition for CreateResponse`. Using (netcore 2.0). – hIpPy Feb 01 '18 at 15:43
  • Use var resp = new HttpResponseMessage(HttpStatusCode.OK) instead – Cinchoo Feb 01 '18 at 16:15