C# 如何将一个流的内容复制到另一个流?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/230128/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 19:05:49  来源:igfitidea点击:

How do I copy the contents of one stream to another?

c#streamcopying

提问by Anton

What is the best way to copy the contents of one stream to another? Is there a standard utility method for this?

将一个流的内容复制到另一个流的最佳方法是什么?是否有标准的实用方法?

采纳答案by Nick

From .NET 4.5 on, there is the Stream.CopyToAsyncmethod

从 .NET 4.5 开始,有Stream.CopyToAsync方法

input.CopyToAsync(output);

This will return a Taskthat can be continued on when completed, like so:

这将返回一个Task可以在完成后继续的,如下所示:

await input.CopyToAsync(output)

// Code from here on will be run in a continuation.

Note that depending on where the call to CopyToAsyncis made, the code that follows may or may not continue on the same thread that called it.

请注意,根据调用的位置,后面CopyToAsync的代码可能会或可能不会在调用它的同一线程上继续。

The SynchronizationContextthat was captured when calling awaitwill determine what thread the continuation will be executed on.

SynchronizationContext调用时被抓获await将决定线程的延续将上执行。

Additionally, this call (and this is an implementation detail subject to change) still sequences reads and writes (it just doesn't waste a threads blocking on I/O completion).

此外,这个调用(这是一个可能会更改的实现细节)仍然对读取和写入进行排序(它只是不会浪费在 I/O 完成时阻塞的线程)。

From .NET 4.0 on, there's is the Stream.CopyTomethod

从 .NET 4.0 开始,有Stream.CopyTo方法

input.CopyTo(output);

For .NET 3.5 and before

对于 .NET 3.5 及更早版本

There isn't anything baked into the framework to assist with this; you have to copy the content manually, like so:

框架中没有任何东西可以帮助解决这个问题;您必须手动复制内容,如下所示:

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

Note 1: This method will allow you to report on progress (x bytes read so far ...)
Note 2: Why use a fixed buffer size and not input.Length? Because that Length may not be available! From the docs:

注 1:此方法将允许您报告进度(到目前为止已读取 x 字节...)
注 2:为什么使用固定缓冲区大小而不是input.Length?因为那个 Length 可能不可用!从文档

If a class derived from Stream does not support seeking, calls to Length, SetLength, Position, and Seek throw a NotSupportedException.

如果从 Stream 派生的类不支持查找,则对 Length、SetLength、Position 和 Seek 的调用将抛出 NotSupportedException。

回答by Tamas Czinege

Unfortunately, there is no really simple solution. You can try something like that:

不幸的是,没有真正简单的解决方案。你可以尝试这样的事情:

Stream s1, s2;
byte[] buffer = new byte[4096];
int bytesRead = 0;
while (bytesRead = s1.Read(buffer, 0, buffer.Length) > 0) s2.Write(buffer, 0, bytesRead);
s1.Close(); s2.Close();

But the problem with that that different implementation of the Stream class might behave differently if there is nothing to read. A stream reading a file from a local harddrive will probably block until the read operaition has read enough data from the disk to fill the buffer and only return less data if it reaches the end of file. On the other hand, a stream reading from the network might return less data even though there are more data left to be received.

但是,如果没有可读取的内容,那么 Stream 类的不同实现可能会表现出不同的问题。从本地硬盘读取文件的流可能会阻塞,直到读取操作从磁盘读取足够的数据以填充缓冲区,并且只有在到达文件末尾时才返回较少的数据。另一方面,即使还有更多的数据要接收,从网络读取的流也可能返回更少的数据。

Always check the documentation of the specific stream class you are using before using a generic solution.

在使用通用解决方案之前,请务必检查您正在使用的特定流类的文档。

回答by fryguybob

The basic questions that differentiate implementations of "CopyStream" are:

区分“CopyStream”实现的基本问题是:

  • size of the reading buffer
  • size of the writes
  • Can we use more than one thread (writing while we are reading).
  • 读取缓冲区的大小
  • 写入的大小
  • 我们可以使用多个线程吗(边读边写)。

The answers to these questions result in vastly different implementations of CopyStream and are dependent on what kind of streams you have and what you are trying to optimize. The "best" implementation would even need to know what specific hardware the streams were reading and writing to.

这些问题的答案导致了 CopyStream 的截然不同的实现,并且取决于您拥有的流类型以及您尝试优化的内容。“最佳”实现甚至需要知道流正在读取和写入的特定硬件。

回答by Coderer

There may be a way to do this more efficiently, depending on what kind of stream you're working with. If you can convert one or both of your streams to a MemoryStream, you can use the GetBuffer method to work directly with a byte array representing your data. This lets you use methods like Array.CopyTo, which abstract away all the issues raised by fryguybob. You can just trust .NET to know the optimal way to copy the data.

可能有一种方法可以更有效地执行此操作,具体取决于您使用的流类型。如果您可以将一个或两个流转换为 MemoryStream,则可以使用 GetBuffer 方法直接处理表示数据的字节数组。这让您可以使用 Array.CopyTo 之类的方法,这些方法可以抽象出 fryguybob 提出的所有问题。您可以相信 .NET 知道复制数据的最佳方式。

回答by Kronass

if you want a procdure to copy a stream to other the one that nick posted is fine but it is missing the position reset, it should be

如果你想要一个程序将一个流复制到另一个 nick 发布的流很好但它缺少位置重置,它应该是

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[32768];
    long TempPos = input.Position;
    while (true)    
    {
        int read = input.Read (buffer, 0, buffer.Length);
        if (read <= 0)
            return;
        output.Write (buffer, 0, read);
    }
    input.Position = TempPos;// or you make Position = 0 to set it at the start
}

but if it is in runtime not using a procedure you shpuld use memory stream

但如果它在运行时不使用程序,你应该使用内存流

Stream output = new MemoryStream();
byte[] buffer = new byte[32768]; // or you specify the size you want of your buffer
long TempPos = input.Position;
while (true)    
{
    int read = input.Read (buffer, 0, buffer.Length);
    if (read <= 0)
        return;
    output.Write (buffer, 0, read);
 }
    input.Position = TempPos;// or you make Position = 0 to set it at the start

回答by hannasm

There is actually, a less heavy-handed way of doing a stream copy. Take note however, that this implies that you can store the entire file in memory. Don't try and use this if you are working with files that go into the hundreds of megabytes or more, without caution.

实际上,有一种不那么严厉的方式来进行流复制。但是请注意,这意味着您可以将整个文件存储在内存中。如果您正在处理数百兆字节或更多的文件,请不要随意尝试使用它。

public static void CopyStream(Stream input, Stream output)
{
  using (StreamReader reader = new StreamReader(input))
  using (StreamWriter writer = new StreamWriter(output))
  {
    writer.Write(reader.ReadToEnd());
  }
}

NOTE: There may also be some issues concerning binary data and character encodings.

注意:二进制数据和字符编码也可能存在一些问题。

回答by Eloff

I use the following extension methods. They have optimized overloads for when one stream is a MemoryStream.

我使用以下扩展方法。当一个流是 MemoryStream 时,它们已经优化了重载。

    public static void CopyTo(this Stream src, Stream dest)
    {
        int size = (src.CanSeek) ? Math.Min((int)(src.Length - src.Position), 0x2000) : 0x2000;
        byte[] buffer = new byte[size];
        int n;
        do
        {
            n = src.Read(buffer, 0, buffer.Length);
            dest.Write(buffer, 0, n);
        } while (n != 0);           
    }

    public static void CopyTo(this MemoryStream src, Stream dest)
    {
        dest.Write(src.GetBuffer(), (int)src.Position, (int)(src.Length - src.Position));
    }

    public static void CopyTo(this Stream src, MemoryStream dest)
    {
        if (src.CanSeek)
        {
            int pos = (int)dest.Position;
            int length = (int)(src.Length - src.Position) + pos;
            dest.SetLength(length); 

            while(pos < length)                
                pos += src.Read(dest.GetBuffer(), pos, length - pos);
        }
        else
            src.CopyTo((Stream)dest);
    }

回答by Joshua

MemoryStream has .WriteTo(outstream);

MemoryStream 有 .WriteTo(outstream);

and .NET 4.0 has .CopyTo on normal stream object.

和 .NET 4.0 在普通流对象上有 .CopyTo 。

.NET 4.0:

.NET 4.0:

instream.CopyTo(outstream);

回答by mdonatas

Since none of the answers have covered an asynchronous way of copying from one stream to another, here is a pattern that I've successfully used in a port forwarding application to copy data from one network stream to another. It lacks exception handling to emphasize the pattern.

由于没有一个答案涵盖从一个流复制到另一个流的异步方式,这里是我在端口转发应用程序中成功使用的一种模式,用于将数据从一个网络流复制到另一个。它缺乏强调模式的异常处理。

const int BUFFER_SIZE = 4096;

static byte[] bufferForRead = new byte[BUFFER_SIZE];
static byte[] bufferForWrite = new byte[BUFFER_SIZE];

static Stream sourceStream = new MemoryStream();
static Stream destinationStream = new MemoryStream();

static void Main(string[] args)
{
    // Initial read from source stream
    sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}

private static void BeginReadCallback(IAsyncResult asyncRes)
{
    // Finish reading from source stream
    int bytesRead = sourceStream.EndRead(asyncRes);
    // Make a copy of the buffer as we'll start another read immediately
    Array.Copy(bufferForRead, 0, bufferForWrite, 0, bytesRead);
    // Write copied buffer to destination stream
    destinationStream.BeginWrite(bufferForWrite, 0, bytesRead, BeginWriteCallback, null);
    // Start the next read (looks like async recursion I guess)
    sourceStream.BeginRead(bufferForRead, 0, BUFFER_SIZE, BeginReadCallback, null);
}

private static void BeginWriteCallback(IAsyncResult asyncRes)
{
    // Finish writing to destination stream
    destinationStream.EndWrite(asyncRes);
}

回答by Jayesh Sorathia

.NET Framework 4 introduce new "CopyTo" method of Stream Class of System.IO namespace. Using this method we can copy one stream to another stream of different stream class.

.NET Framework 4 引入了 System.IO 命名空间的 Stream 类的新“CopyTo”方法。使用这种方法,我们可以将一个流复制到不同流类的另一个流。

Here is example for this.

这是一个例子。

    FileStream objFileStream = File.Open(Server.MapPath("TextFile.txt"), FileMode.Open);
    Response.Write(string.Format("FileStream Content length: {0}", objFileStream.Length.ToString()));

    MemoryStream objMemoryStream = new MemoryStream();

    // Copy File Stream to Memory Stream using CopyTo method
    objFileStream.CopyTo(objMemoryStream);
    Response.Write("<br/><br/>");
    Response.Write(string.Format("MemoryStream Content length: {0}", objMemoryStream.Length.ToString()));
    Response.Write("<br/><br/>");