Creating a text file on the fly and have it download/save on client side
Asked Answered
M

1

2

In our ASP.NET Core 1.1, EF Core 1.1 app we have populated a string with some data from SQL Server and would like to have a text file created out of it on the fly and have a user save/download from the client side.

In old days we used to do it as follows:

List<string> stringList = GetListOfStrings();

MemoryStream ms = new MemoryStream();
TextWriter tw = new StreamWriter(ms);

foreach (string s in stringList) {
    tw.WriteLine(s);
}
tw.Flush();
byte[] bytes = ms.ToArray();
ms.Close();

Response.Clear();
Response.ContentType = "application/force-download";
Response.AddHeader("content-disposition", "attachment; filename=myFile.txt");
Response.BinaryWrite(bytes);
Response.End();

How can that be achieved in ASP.NET Core 1.1? Tried to follow File Providers in ASP.NET Core to no avail.

Marcela answered 31/5, 2017 at 20:45 Comment(4)
shouldn't the Response.ContentType be Response.ContentType = "application/download" or change it to Response.ContentType = "text";Thrave
Are you using Web API?Everyman
@Thrave application/downloadwill also be ok.Marcela
@Amy. No just a regular ASP.Net Core 1.1 app.Marcela
H
1
MemoryReader mr = new MemoryStream();
TextWriter tw = new StreamWriter(mr);

foreach (string s in stringList) {
    tw.WriteLine(s);
}
tw.Flush();

return File(mr, "application/force-download", "myFile.txt")

Or directly write to the response:

HttpContext.Response.Body.WriteAsync(...);

Also viable alternative

TextWriter tw = new StreamWriter(HttpContext.Response.Body);

so you can directly write to the output string, without any additional memory usage. But you shouldn't close it, since it's the connection to the browser and may be needed further up in the pipeline.

Hale answered 31/5, 2017 at 20:57 Comment(8)
on return File(tw,...) line it's giving an error: Cannot convert from TextWriter to byte[]Marcela
Then just pass the memory stream to it. File has an overload which accepts StreamHale
Of course you can also do new StreamWriter(HttpContext.Response.Body) to write to the stream, instead of using WriteAsync which only accepts bytesHale
I first declared MemoryStream ms = new MemoryStream();, then passed ms to using(TextWriter tw = new StreamWriter(ms)){...}. But then return File(ms, ....) returns blank page. However, in addition, if I also add byte[] bytes = ms.ToArray(); and ms.Flush(); and then return File(bytes, ....)` your code works.Marcela
Calling ToArray should be unnecessary, as you would double your ram usage and the whole thing would be quite inefficient (write to memory, then copy it and only then pass it). Dunno about flush. Can't test right now, but flushing should be unnecessary unless it's a buffered streamHale
Removing Flush() was fine but w/o byte[] bytes = ms.ToArray(); and then calling return File(ms, ....) gives the error: Cannot access a closed StreamMarcela
You shouldn't close it before you have read it. Also now thinking about it, you need to rewind the memory stream if you pass the stream to the File method, since after you written to it it'S at the end. Setting .Position = 0 of the memory stream should do it. But liek I said, using the Responsestream for StreamWrite may be most effective as no memory allocation is necessaryHale
Following did the trick: MemoryStream ms = new MemoryStream(); TextWriter tw = new StreamWriter(ms); await tw.WriteAsync(csv); tw.Flush(); ms.Position = 0; return File(ms, "application/force-download", "myFile.csv"); The Using(...){...} closes ms that was giving the error Cannot access a closed Stream. You may want to re-format your response for the benefit of other readers - although I did understand your thoughts due to our back and forth discussion. And I'll mark it as an Answer. Also, new StreamWriter(HttpContext.Response.Body) works but I needed to have user download fileMarcela

© 2022 - 2024 — McMap. All rights reserved.