如何在.NET中将一个流的内容写入另一个流?

41

我经常遇到这样的问题:我有一个充满数据的流,希望将其全部写入另一个流。

所有的代码示例都使用字节数组形式的缓冲区。

是否有更优雅的方法?

如果没有,请问缓冲区的理想大小是多少。哪些因素构成了这个值?

7个回答

73

在 .NET 4.0 中,我们终于得到了一个 Stream.CopyTo 方法!太棒了!


2
我反编译了Stream.CopyTo方法,它调用了“this.InternalCopyTo(destination, 81920);”,其中整数值为缓冲区大小。与大多数缓冲技术使用的标准4k相比,这个缓冲区大小似乎很大。有人可以对此发表评论吗? - ctrlplusb
2
看起来该数字只是尝试避免使用大对象堆,而该堆将在略高于该数量处启动。 - James World
81920看起来几乎像是一个打字错误,但我怀疑@JamesWorld是正确的,他们选择了一个足够小以避免LOH,但足够大以对长流有效,并且对短流不会太差的值。如果您有一个短流,那么您可以使用接受缓冲区大小的CopyTo()重载。 - redcalx

16

关于理想的缓冲区大小:

"使用Read方法时,使用与流的内部缓冲区大小相同的缓冲区更有效,其中内部缓冲区设置为所需的块大小,并始终读取少于块大小。如果在构建流时未指定内部缓冲区的大小,则其默认大小为4千字节(4096字节)."

任何流读取过程都将使用Read(char buffer[], int index, count)方法,这是该引用所指的方法。

http://msdn.microsoft.com/en-us/library/9kstw824.aspx (在“备注”下)。


8

我不确定在.NET中是否可以直接将一个流传输到另一个流中,但以下是一种使用中间字节缓冲区实现的方法。缓冲区的大小是任意的,最有效的大小取决于传输的数据量。

static void CopyStream(Stream input, Stream output){
    byte[] buffer = new byte[0x1000];
    int read;
    while ((read = input.Read(buffer, 0, buffer.Length)) > 0) 
        output.Write(buffer, 0, read);
}

1
你可以在C# 3.0中将其实现为扩展方法,并将其定义为类似于static void CopyTo(this Stream input, Stream output)的形式... - Roger Lipscombe

5

BufferedStream.CopyTo(Stream)


2

2
我不知道有比使用缓冲区更优雅的方法。
但是,缓冲区的大小可能会有所不同。还记得关于Vista文件复制的问题吗?它的原因(基本上)是改变了缓冲区的大小。这些变化在这篇博客文章中有详细解释。您可以从那篇文章中了解主要因素。然而,这仅适用于文件复制。在应用程序中,您可能会进行很多内存复制,在这种情况下,4KB可能是最好的缓冲区大小,正如.NET文档推荐的那样

0
如一些人所建议的那样,可以使用 CopyToCopyToAsync 方法来完成此工作。这里提供一个示例,展示了一个在端口30303上监听外部连接并将其与本地端口8085进行连接的TCP服务器(使用 .NET 5 编写)。大多数流应该都可以使用相同的方式处理,只需注意它们是双向还是单向的即可。
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var externalConnectionListener = new TcpListener(IPAddress.Any, 30303);
            externalConnectionListener.Start();

            while (true)
            {
                var externalConnection = await externalConnectionListener.AcceptTcpClientAsync().ConfigureAwait(false);

                _ = Task.Factory.StartNew(async () =>
                  {
                      using NetworkStream externalConnectionStream = externalConnection.GetStream();
                      using TcpClient internalConnection = new TcpClient("127.0.0.1", 8085);
                      using NetworkStream internalConnectionStream = internalConnection.GetStream();

                      await Task.WhenAny(
                                externalConnectionStream.CopyToAsync(internalConnectionStream),
                                internalConnectionStream.CopyToAsync(externalConnectionStream)).ConfigureAwait(false);

                  });

            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接