Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stream Reuse in C#

Tags:

c#

I've been playing around with what I thought was a simple idea. I want to be able to read in a file from somewhere (website, filesystem, ftp), perform some operations on it (compress, encrypt, etc.) and then save it somewhere (somewhere may be a filesystem, ftp, or whatever). It's a basic pipeline design. What I would like to do is to read in the file and put it onto a MemoryStream, then perform the operations on the data in the MemoryStream, and then save that data in the MemoryStream somewhere. I was thinking I could use the same Stream to do this but run into a couple of problems:

  1. Everytime I use a StreamWriter or StreamReader I need to close it and that closes the stream so that I cannot use it anymore. That seems like there must be some way to get around that.
  2. Some of these files may be big and so I may run out of memory if I try to read the whole thing in at once.

I was hoping to be able to spin up each of the steps as separate threads and have the compression step begin as soon as there is data on the stream, and then as soon as the compression has some compressed data available on the stream I could start saving it (for example). Is anything like this easily possible with the C# Streams? ANyone have thoughts as to how to accomplish this best?

Thanks,

Mike

like image 675
MikeD Avatar asked Jun 10 '09 03:06

MikeD


2 Answers

Using a helper method to drive the streaming:

static public void StreamCopy(Stream source, Stream target)
{
    byte[] buffer = new byte[8 * 1024];

    int size;
    do
    {
      size = source.Read(buffer, 0, 8 * 1024);
      target.Write(buffer, 0, size);
    } while (size > 0);
}

You can easily combine whatever you need:

using (FileStream iFile = new FileStream(...))
using (FileStream oFile = new FileStream(...))
using (DeflateStream oZip = new DeflateStream(outFile, CompressionMode.Compress))
    StreamCopy(iFile, oZip);

Depending on what you are actually trying to do, you'd chain the streams differently. This also uses relatively little memory, because only the data being operated upon is in memory.

like image 168
jerryjvl Avatar answered Sep 20 '22 02:09

jerryjvl


StreamReader/StreamWriter shouldn't have been designed to close their underlying stream -- that's a horrible misfeature in the BCL. But they do, they won't be changed (because of backward compatibility), so we're stuck with this disaster of an API.

But there are some well-established workarounds, if you want to use StreamReader/Writer but keep the Stream open afterward.

  • For a StreamReader: don't Dispose the StreamReader. It's that simple. It's harmless to just let a StreamReader go without ever calling Dispose. The only effect is that your Stream won't get prematurely closed, which is actually a plus.
  • For a StreamWriter: there may be buffered data, so you can't get away with just letting it go. You have to call Flush, to make sure that buffered data gets written out to the Stream. Then you can just let the StreamWriter go. (Basically, you put a Flush where you normally would have put a Dispose.)
like image 34
Joe White Avatar answered Sep 21 '22 02:09

Joe White