7

This is updated question, there used to be a bug in my code

I would like to be able to send chunks of data over to the client.

Anything will be appreciated.

Is there a way to provide to asp.net core more control to how it streams the data.

I am worried how the below code scales.

Could someone please advise how to go streaming data through a web api in asp.net core?

The answer that was provided and the code below works. I am not sure how it scales though?

Is it possible to retrieve chunks of data and write them to the request, with only getting the chunks into memory. So i would be able to download very large files.

using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    return File(System.IO.File.OpenRead(filePath), "audio/mpeg");
}
panoskarajohn
  • 1,846
  • 1
  • 21
  • 37
  • That's how streaming works, only small chanks are being read to the memory and trasferred over the network. To make it scale even more you can use use async api to open the file stream. That's 5th parameter IIRC. – Andrii Litvinov Dec 06 '19 at 08:36
  • @AndriiLitvinov can you provide more info about async api? – panoskarajohn Dec 08 '19 at 11:59
  • 1
    Sure, here it is https://stackoverflow.com/a/13168006/2138959. There is a flag `FileOptions.Asynchronous`. – Andrii Litvinov Dec 08 '19 at 13:39

2 Answers2

5

Applying the FileStream approach - as already mentioned - use the FileStream constructor that accepts a bufferSize argument, which specifies the amount of bytes being read into memory.
(You can overrule the default value (4096) to fit your environment.)

public FileStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize);

bufferSize:
A positive System.Int32 value greater than 0 indicating the buffer size.
The default buffer size is 4096.

public IActionResult GetFile()
{ 
    var filePath = @"c:\temp\file.mpg"; // Your path to the audio file.
    var bufferSize = 1024;
    var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize);            
    return File(fileStream, "audio/mpeg");
}

Note that there's no need to dispose the fileStream; the File method takes care of this.


To clarify:

When passing in a FileStream, its content is being read in chunks (matching the configured buffersize).
Concrete, this means that its Read method (int Read (byte[] array, int offset, int count)) gets executed repeatedly untill all bytes have been read, ensuring that no more than the given number of bytes are stored in memory.

So the scalability is within the less memory usage, as memory is a resource that can come under pressure if the size of the file is high, especially in combination with a high read frequency (of this or of other files) which might cause out of memory problems.

pfx
  • 20,323
  • 43
  • 37
  • 57
  • Could you please provide information about the Scalability of the code(what to be careful sth like this)? When i asked this question my biggest issue that I do not know what is goin on behind the scenes? I managed to work a solution on my own, which is very tricky and uses RestSharp(also implemented javascript). Honestly i feel the question i am sking cannot be answered here. Thank you for your answer though. – panoskarajohn Dec 06 '19 at 10:21
  • thank you for your update. This makes a lot of sense. I would like for more answers to come. Yours is the most complete so far. – panoskarajohn Dec 06 '19 at 12:08
1

Posting as a community wiki, since it doesn't technically answer the question, but suggested code won't work as a comment.

You can return a stream directly from FileResult, so there's no need to manually read from it. In fact, your code doesn't actually "stream", since you're basically reading the whole stream into memory, and then returning the byte[] at the end. Instead, just do:

using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
    return File(fileStream, "audio/mpeg");
}

Or even simpler, just return the file path, and let FileResult handle it completely:

return File(System.IO.File.OpenRead(filePath), "audio/mpeg");
panoskarajohn
  • 1,846
  • 1
  • 21
  • 37
Chris Pratt
  • 232,153
  • 36
  • 385
  • 444