Introduction
This article represents a simple Stream class whose data is based on the number of other .NET Framework streams.
There are some tasks when you may want to provide the data combined from different parts via the Stream interface. The most obvious way is to create the MemoryStream class instance and copy all data parts (streams in our case) into this single object. This solution has only one good thing - simplicity, and you can use it for sure for combining small footprint streams without bothering about the reverse of the medal. At the same time, when working with large-size streams or having a large number of streams, such primitive copying of data chunks may lead to bad application performance and irrational use of virtual memory.
Declaration of MultiStream Class
In order to effectively combine a number of streams into a single Stream class descendant, you will need to organize access to stream data in appropriate order.
We will create a new MultiStream inherited from the .NET base abstract Stream class. Methods Read, Seek, Write, and Length properties need to be overridden and implemented within the MultiStream class. All remaining abstract Stream members have the obvious implementation, and to be short, we do not place them here. But you can find the full source code for this class at multistreamcs.zip.
The code below shows the implementation of the Read method of the MultiStream class.
public override int Read(byte[] buffer, int offset, int count)
{
long len = 0;
int result = 0;
int buf_pos = offset;
int bytesRead;
foreach (Stream stream in streamList)
{
if (position < (len + stream.Length))
{
stream.Position = position - len;
bytesRead = stream.Read(buffer, buf_pos, count);
result += bytesRead;
buf_pos += bytesRead;
position += bytesRead;
if (bytesRead < count)
{
count -= bytesRead;
}
else
{
break;
}
}
len += stream.Length;
}
return result;
}
This method walks through all streams being stored within the streamList member of the MultiStream class and checks each of them for matching the MultiStream Position. If the current stream position plus the total of all of the previous stream sizes matches the main Position, then data from this stream will be transferred until the requested buffer size extends up to the other stream items.
The Seek method implements locating the TMultiStream Position pointer using the total size of collected stream items.
public override long Seek(long offset, SeekOrigin origin)
{
long len = Length;
switch (origin)
{
case SeekOrigin.Begin:
position = offset;
break;
case SeekOrigin.Current:
position += offset;
break;
case SeekOrigin.End:
position = len - offset;
break;
}
if (position > len)
{
position = len;
}
else if (position < 0)
{
position = 0;
}
return position;
}
public override bool CanWrite
{
get { return false; }
}
Indeed it is quite difficult to imagine such a task when you might need to change the contents of the multistream object.
Usage of MultiStream Class
With just a few lines of code, the new MultiStream class is ready to use. Let's go ahead and test it.
MultiStream stream = new MultiStream();
stream.AddStream(new StringStream("test string "));
stream.AddStream(new FileStream("testfile.txt", FileMode.Open, FileAccess.Read, FileShare.Read));
byte[] buf = new byte[1024];
int cnt = stream.Read(buf, 0, 1024);
Debug.Assert(new ASCIIEncoding().GetString(buf, 0, cnt).Equals("test string test file"));
This code is constantly being improved, and your comments and suggestions are always welcome.
Casper BommeléPosted Apr 13, 2022, 8:39 AM
Public override void Close() { FreeStreams(); base.Close(); } public void FreeStreams() { foreach (Stream stream in streamList) { stream.Close(); } }
Casper BommeléPosted Apr 13, 2022, 8:39 AM
I found this code useful, however I noticed that the filestreams that are combined, are not properly closed upon disposal of the multi stream, so I added this:
sumanth poojaryPosted Jun 8, 2017, 6:42 AM
I want video chat functionality. One user is web and another user is ios. Intermediate is Web API. Now I don't to use 3rd party tool. Could you please explain how to read both video stream and merge, so we can video chat using web api.
WilliameditedPosted Apr 14, 2010, 2:12 AMEdited Apr 14, 2010, 2:13 AM
Line 59 of MultiStream.cs should be: position = len + offset; Using plus instead of minus will keep it consistent with the Stream.Seek method when seeking from the end since a negive value should be passed: Stream.Seek(-100, SeekOrigin.End) Other than that, awesome job!