Menu Menu
File streaming

File streaming

We were working on the project that was hosted on a Cloud Service and had assets such as files and videos also hosted in a Cloud. One of the requirements was that we download files from the Cloud service and serve them to users. The issue here was that users didn't want to wait for us to download the file in order for their download to start. We also didn't want to download complete files before starting to serve them to the users because some files were quite big and their download would consume significant amounts of RAM memory. Instead, we decided to stream files from a remote server directly to users, so in this way the user would be able to see the progress, and we would only load small amounts of data into the memory.

First, we need to open an input stream:

WebRequest req = HttpWebRequest.Create(url);
WebResponse response = req.GetResponse();
//We get response and check for content type
string contentType = response.ContentType;
Stream stream = response.GetResponseStream();

After that, we need to initialize data streaming buffer, with some additional variables:

int length;
byte[] buffer = new Byte[10000];
long dataToRead;

Length will be used to store information about how many bytes we have read from the stream, and dataToRead is used to check if we have read all of the data.

After that we set the response ContentType and headers:

//set content type and file name
Response.ContentType = contentType;
Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);

Finally when we start the streaming process, we will read the bytes until we read all the data and after that we will close both streams.

//streaming
            try
            {
                //In some cases content length is not given in header.
                dataToRead = response.ContentLength;                

                //read stream until the end or until client is connected
                while (dataToRead > 0)
                {
                    if (ControlerResponse.IsClientConnected)
                    {
                        length = stream.Read(buffer, 0, 10000);
                        Response.OutputStream.Write(buffer, 0, length);
                        //it's important to flush after each chunk in order to free up memory
                        Response.Flush();
                        dataToRead = dataToRead - length;
                    }
                    else
                    {
                        dataToRead = -1;
                    }
                }
            }
            catch (Exception ex)
            {
                // Trap the error, if any.
                Response.Write("Error : " + ex.Message);
            }
            finally
            {
                //close both streams
                if (stream != null)
                {
                    //Close the file.
                    stream.Close();
                }
                Response.Close();
            }

Note: if FileStream is used to stream a file from disk, length property can be used to determine the size of the string, but we need to be careful because not all streams support seek operations.

//In example on msdn site they use stream.Length property, but you need to make sure that stream supports seek operations
//More details http://stackoverflow.com/questions/3434007/error-this-stream-does-not-support-seek-operations-in-c-sharp
if (stream.CanSeek)
{
dataToRead = stream.Length;
}

Note 2 We might not have Content Length set in header. In this case we could try reading the data until length is lower than 10000, because that will indicate that we have read all of the streams' data.

do
{
if (!ControlerResponse.IsClientConnected)
break;

length = stream.Read(buffer, 0, 10000);
       ControlerResponse.OutputStream.Write(buffer, 0, length);
       //it's important to flush after each chunk in order to free up memory
       ControlerResponse.Flush();
} while (length == 10000);
Latest blog posts
ITkonekt 2019 Overview
Front-End Day 2019: Our Impressions
Workshop with Uncle Bob
International Umbraco Conference in Novi Sad: Umbraco Roadshow 2019
How To Send an Email with Attachments Using EPiServer Forms