There are a lot of places on the internet that show how to return files but I have found none that will return dynamically generated binary data without storing the whole contents on memory. Maybe I should serialize my data using Json instead of protobufers.
Thanks to this question I was able to create something like this:
[HttpGet]
public ActionResult DownloadItems()
{
// get 100K items from database as IEnumerable.
IEnumerable<SomeObject> items = myDatabase.query("my query that returns 100K objects");
// create memory stream where to place serialized items
MemoryStream ms = new ();
// write all serialized items to stream
foreach(var item in items)
{
byte[] itemSerialized = item.BinarySerialize();
ms.Write(itemSerialized,0,itemSerialized.Length);
}
// set position to the begining of memory stream
ms.Position = 0;
return File(ms, "application /octet-stream", "foo.bin");
}
This works well but I am loading 100K items into memory. My question is how can I return the same dynamically generated file without having to load all the items into memory?
I remember that the HTTP protocol returns something like this when returning binary files:
HTTP response headers
...
---------SomeGUID--------------
.. binary data
---------SomeGUID--------------
as a result I believe that having something like this will make it work (it has pseudo code):
[HttpGet]
public ActionResult DownloadItems()
{
// get 100K items from database as IEnumerable.
IEnumerable<SomeObject> items = myDatabase.query("my query that returns 100K objects");
// write the begining of file (PSEUDO code)
this.response.body.writeString("-----------------SomeGuid------------");
// write all serialized items to stream
foreach(var item in items)
{
byte[] itemSerialized = item.BinarySerialize();
this.response.body.write(itemSerialized,0,itemSerialized.Length);
}
// set position to the begining of memory stream
ms.Position = 0;
this.response.body.writeString("-----------------SomeGuid------------");
}
I can install fiddler or any other proxy to see how the real binary transfer of a file looks like. But is there a build in way of doing that so I don’t have to go through all that trouble?
2
Answers
I just created my own fake file stream for this to work:
and My endpoint looks like this:
Edit
Do not use sugested
FakeFileStream
. For some reason it gave me problems. Maybe because it was writting to the stream very few bytes.Anywyas I was not able to do this from a controller. But I was able to do it using middleware. I had to do simething like this:
Rather than trying to reuse
File()
/FileStreamResult
, I would recommend implementing your ownActionResult
and rendering the content to the response stream there.Or you could go one step further and implement a custom formatter.