File streaming

Nemanja Malocic Categories: .net development Date 15-Aug-2015
File Streaming

Table of contents

    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 typestring 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 nameResponse.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-sharpif (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);

    nemanja-maloc-ic-_authorsphoto.png
    Nemanja Malocic Software Developer

    Born to a family of lawyers, Nemanja knew from a young age that it was his destiny to leave the life of litigation for a life of the imagination.