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

C# 如何将一个流的内容复制到另一个流?,c#,stream,copying,C#,Stream,Copying,将一个流的内容复制到另一个流的最佳方式是什么?有标准的实用方法吗?从.NET 4.5开始,有 这将返回一个在完成时可以继续的,如下所示: await input.CopyToAsync(output) // Code from here on will be run in a continuation. public static void CopyStream(Stream input, Stream output) { byte[] buffer = new byte[32768

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

从.NET 4.5开始,有

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

await input.CopyToAsync(output)

// Code from here on will be run in a continuation.
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);
    }
}
请注意,根据调用
CopyToAsync
的位置,后面的代码可能会在调用它的同一线程上继续,也可能不会继续

调用时捕获的线程将确定将在哪个线程上执行延续

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

从.NET 4.0开始,就有了

适用于.NET 3.5及之前版本

框架中没有任何东西可以帮助实现这一点;您必须手动复制内容,如下所示:

await input.CopyToAsync(output)

// Code from here on will be run in a continuation.
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);
    }
}
注1:此方法允许您报告进度(到目前为止读取的x字节…)
注2:为什么使用固定的缓冲区大小而不是
input.Length
?因为该长度可能不可用!从:

若从流派生的类不支持查找,则调用Length、SetLength、Position和Seek会抛出NotSupportedException


不幸的是,没有真正简单的解决方案。您可以尝试类似的方法:

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


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

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

  • 读取缓冲区的大小
  • 写入的大小
  • 我们可以使用多个线程(边读边写)吗

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

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

如果您想要一个将流复制到另一个流的过程,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
}
publicstaticvoidcopystream(流输入、流输出)
{
字节[]缓冲区=新字节[32768];
长TempPos=输入位置;
while(true)
{
int read=input.read(buffer,0,buffer.Length);

如果(读取实际上有一种不太繁重的方式来进行流复制。但是请注意,这意味着您可以将整个文件存储在内存中。如果您处理的文件大小达到数百MB或更大,请不要尝试使用这种方式,不要小心谨慎

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

注意:可能还有一些关于二进制数据和字符编码的问题。

我使用以下扩展方法。当一个流是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);
    }
publicstaticvoidcopyto(此流src,流dest)
{
int size=(src.CanSeek)?Math.Min((int)(src.Length-src.Position),0x2000:0x2000;
字节[]缓冲区=新字节[大小];
int n;
做
{
n=src.Read(buffer,0,buffer.Length);
目标写入(缓冲区,0,n);
}而(n!=0);
}
公共静态void CopyTo(此MemoryStream src,Stream dest)
{
dest.Write(src.GetBuffer(),(int)src.Position,(int)(src.Length-src.Position));
}
公共静态void CopyTo(此流src、MemoryStream dest)
{
if(src.CanSeek)
{
内部位置=(内部)目标位置;
int length=(int)(src.length-src.Position)+pos;
目的设定长度(长度);
while(pos
MemoryStream
具有
.WriteTo(outtream);

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

.NET 4.0:

instream.CopyTo(outstream);

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

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);
}

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

下面是一个例子

    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/>");
FileStream objFileStream=File.Open(Server.MapPath(“TextFile.txt”)、FileMode.Open);
Write(string.Format(“FileStream内容长度:{0}”,objFileStream.length.ToString());
MemoryStream objMemoryStream=新的MemoryStream();
//使用CopyTo方法将文件流复制到内存流
CopyTo(objMemoryStream);
响应。写(“

”); Write(string.Format(“MemoryStream内容长度:{0}”,objMemoryStream.length.ToString()); 响应
    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/>");
MemoryStream1.WriteTo(MemoryStream2);
    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
Stream stream = new MemoryStream();
MemoryStream newMs = (MemoryStream)stream;

byte[] getByte = newMs.ToArray();