locked
Write a stream to a file RRS feed

  • Question

  • I must be missing something, but as far as I can tell there is no way to write a stream to a file without having to manually read in to a buffer and then writing it out to a file, is this right? If so is there some sort of rationale for this?

    As a trivial example, consider something like: File.OpenWrite( "oput.txt" ).WriteStream( File.OpenRead( "input.txt" ) )
    But there is no WriteStream method so I have to read a bunch of bytes from the first stream (using Read) and manually write it out to the other stream (using Write). 

    Now obviously I'm not just copying the file, I'm attaching other kinds of stream "transformers" first, but it seems like there really ought to be a way of taking my input stream and just directly piping it into an output stream? What am I missing?


    Thursday, May 21, 2009 10:25 PM

Answers

  • > way to write a stream to a file without having to manually read

    Microsoft did not include such a method, but as you said you can easily implement it yourself.

            private static void CopyStream(Stream src, Stream dest)
            {
                byte[] buf = new byte[8192];
    
                for (;;)
                {
                    int numRead = src.Read(buf, 0, buf.Length);
                    if (numRead == 0)
                        break;
                    dest.Write(buf, 0, numRead);
                }
            }


    > calling WriteAll would result in an infinite loop

    These cases, of course, are going to require specialized code.
    • Proposed as answer by Tergiver Friday, May 22, 2009 12:58 AM
    • Marked as answer by Harry Zhu Thursday, May 28, 2009 10:04 AM
    Thursday, May 21, 2009 11:03 PM
  • Here's a handy extension method version:

    using System;
    using System.IO;
    
    class Program
    {
        static void Main(string[] args)
        {
            using (var memoryStream = new MemoryStream(new byte[] { 0, 1, 2, 3, 4, 5 }))
            using (var fileStream = new FileStream("C:\\Temp\\stream.bin", FileMode.Create))
                memoryStream.CopyTo(fileStream);
        }
    }
    
    public static class StreamExtensions
    {
        public static void CopyTo(this System.IO.Stream src, System.IO.Stream dest)
        {
            if (src == null)
                throw new System.ArgumentNullException("src");
            if (dest == null)
                throw new System.ArgumentNullException("dest");
    
            System.Diagnostics.Debug.Assert(src.CanRead, "src.CanRead");
            System.Diagnostics.Debug.Assert(dest.CanWrite, "dest.CanWrite");
    
            int readCount;
            var buffer = new byte[8192];
            while ((readCount = src.Read(buffer, 0, buffer.Length)) != 0)
                dest.Write(buffer, 0, readCount);
        }
    }
    
    • Proposed as answer by Matthew Watson Friday, May 22, 2009 9:12 AM
    • Marked as answer by Harry Zhu Thursday, May 28, 2009 10:04 AM
    Friday, May 22, 2009 1:00 AM

All replies

  • File.WriteAll Bytes, Lines or Text.
    Thursday, May 21, 2009 10:44 PM
  • Also the MemoryStream class has WriteTo method that does what you need (i.e. write all bytes to target stream). But unfortunately there is no general "WriteAll" method. And I agree that it might be useful.
    Thursday, May 21, 2009 10:50 PM
  • I have a Stream, not bytes, lines, nor text. 
    Thursday, May 21, 2009 10:52 PM
  • MemoryStream also seems to require that I first read the entire file into memory into a byte buffer (or have a loop where I read N bytes over and over)... I can do that directly by using Read on the source and Write on the target, but I just thought this would be an obvious method that surely must exist somewhere? Or at least there must be a very good reason for why it's missing? I mean, if you're writing "stream oriented" IO it seems pretty common that you'd want to compose a whole bunch of streams in a pipeline without ever needing to manually manage any buffers  (e.g. File -> Text -> Sanitize -> Encrypt -> Zip -> OutputFile )?
    Thursday, May 21, 2009 10:55 PM
  • As I said, there's no way to do it easily and I agree with you that it would be an useful feature. But you have to keep in mind that not all streams are as simple as your ordinary file or memory stream. For example you could have a network stream that sends bytes when they're available and possibly never ends -- calling WriteAll would result in an infinite loop. Or you could theoretically have a random number generator implemented as stream that would supply you with never ending stream of random numbers. Etc.
    • Proposed as answer by Dave Fellows Wednesday, September 5, 2012 10:27 PM
    Thursday, May 21, 2009 10:58 PM
  • But both of those issues would be a problem for loads of other APIs (such as TextReader.ReadToEnd) so they don't seem to mind that problem so far. 

    It's not like it's hard to just read 32K at a time into a byte[] buffer I manage myself and then immediately write it again but it does seem like a glaring omission and I'm wondering if there's some sort of subtle rationale for not allowing it...
    Thursday, May 21, 2009 11:03 PM
  • > way to write a stream to a file without having to manually read

    Microsoft did not include such a method, but as you said you can easily implement it yourself.

            private static void CopyStream(Stream src, Stream dest)
            {
                byte[] buf = new byte[8192];
    
                for (;;)
                {
                    int numRead = src.Read(buf, 0, buf.Length);
                    if (numRead == 0)
                        break;
                    dest.Write(buf, 0, numRead);
                }
            }


    > calling WriteAll would result in an infinite loop

    These cases, of course, are going to require specialized code.
    • Proposed as answer by Tergiver Friday, May 22, 2009 12:58 AM
    • Marked as answer by Harry Zhu Thursday, May 28, 2009 10:04 AM
    Thursday, May 21, 2009 11:03 PM
  • Here's a handy extension method version:

    using System;
    using System.IO;
    
    class Program
    {
        static void Main(string[] args)
        {
            using (var memoryStream = new MemoryStream(new byte[] { 0, 1, 2, 3, 4, 5 }))
            using (var fileStream = new FileStream("C:\\Temp\\stream.bin", FileMode.Create))
                memoryStream.CopyTo(fileStream);
        }
    }
    
    public static class StreamExtensions
    {
        public static void CopyTo(this System.IO.Stream src, System.IO.Stream dest)
        {
            if (src == null)
                throw new System.ArgumentNullException("src");
            if (dest == null)
                throw new System.ArgumentNullException("dest");
    
            System.Diagnostics.Debug.Assert(src.CanRead, "src.CanRead");
            System.Diagnostics.Debug.Assert(dest.CanWrite, "dest.CanWrite");
    
            int readCount;
            var buffer = new byte[8192];
            while ((readCount = src.Read(buffer, 0, buffer.Length)) != 0)
                dest.Write(buffer, 0, readCount);
        }
    }
    
    • Proposed as answer by Matthew Watson Friday, May 22, 2009 9:12 AM
    • Marked as answer by Harry Zhu Thursday, May 28, 2009 10:04 AM
    Friday, May 22, 2009 1:00 AM
  • Tergiver's extension is pretty much what we use. It's nice and convenient.
    Friday, May 22, 2009 9:12 AM