3

Currently I have this code

SessionStream(Request.Content.ReadAsStreamAsync(), new { });

I need to somehow "mirror" the incoming stream so that I have two instances of it. Something like the following pseudo code:

Task<Stream> stream = Request.Content.ReadAsStreamAsync();

SessionStream(stream, new { });

Stream theotherStram;

stream.Result.CopyToAsync(theotherStram)

ThoOtherStream(theotherStram, new { });
user3077725
  • 783
  • 1
  • 10
  • 17
  • possible duplicate of [Best way to copy between two Stream instances](http://stackoverflow.com/questions/230128/best-way-to-copy-between-two-stream-instances) – Bogdan Maxim May 23 '14 at 12:42
  • 1
    @BogdanMaxim that actually is not not good because the "input" is being already used and you end-up with two concurrent copyTo running in parallel. It is not enough to do a search with a similar words, you should be able to understand the topic of the question before posting a duplicate... – user3077725 May 23 '14 at 13:02
  • The suggested duplicate is not duplicate at all. This question is about copying to two streams which is much harder. – usr May 23 '14 at 14:02

2 Answers2

3

A technique that always works is to copy the stream to a MemoryStream and then use that.

Often, it is more efficient to just seek the original stream back to the beginning using the Seek method. That only works if this stream supports seeking.

If you do not want to buffer and cannot seek you need to push the stream contents blockwise to the two consumers. Read a block, write it two times.

If in addition you need a pull model (i.e. hand a readable stream to some component) it gets really hard and threading is involved. You'd need to write a push-to-pull adapter which is always hard.

Noctis
  • 11,507
  • 3
  • 43
  • 82
usr
  • 168,620
  • 35
  • 240
  • 369
3

The answer of usr is still correct in 2020, but for those wondering about why it is not trivial here is a short explanation.

The idea behind the steams is that writing to the stream and reading from it are independent. Usually, the process of reading is much faster then writing (think about receiving data through network - you can read the data as soon as it arrives) so usually the reader waits for new portion of data, processes it as soon as it arrives and then drops it to free the memory, and then waits for the next portion.

This allows processing potentially infinite data stream (for example, application log stream) without using much RAM.

Suppose now we have 2 readers (as required by the question). A data portion arrives, and then we have to wait for both the readers to read the data before we can drop it. Which means that it must be stored in memory until both readers are done with it. The problem is that the readers can process the data with very different speed. E.g. one can write it to a file, another can just count the symbols in memory. In this case either the fast one would have to wait for the slow one before reading further, or we would need to save the data to a buffer in memory, and let the readers read from it. In the worst case we will end up with the full copy of the input stream in memory, basically creating an instance of memory stream.

To implement the first option, you would have to implement a stream reader that is aware which of your stream usage is faster, and considering it, would distribute and drop the data accordingly.

If you are sure you have enough memory, and processing speed is not critical, just use the memory stream:

  using var memStream = new MemoryStream();
  await incomingStream.CopyToAsync(memStream);

  UseTheStreamForTheFirstTime(memStream);

  memStream.Seek(0, SeekOrigin.Begin);
  UseTheStreamAnotherTime(memStream);
Maxim Gritsenko
  • 2,396
  • 11
  • 25