如何将一个stream的内容复制到另一个?
将一个stream的内容复制到另一个stream的最佳方式是什么? 有没有一个标准的实用工具方法呢?
从.NET 4.5开始,有Stream.CopyToAsync
方法
input.CopyToAsync(output);
这将返回一个可以继续完成的Task
,如下所示:
await input.CopyToAsync(output) // Code from here on will be run in a continuation.
请注意,根据调用CopyToAsync
位置,下面的代码可能会或可能不会在调用它的同一个线程上继续。
在调用await
时捕获的SynchronizationContext
将决定继续执行的线程。
此外,这个调用(这是一个实现细节可能会改变)仍然顺序读写(它只是不浪费一个线程阻塞I / O完成)。
从.NET 4.0开始,就有了Stream.CopyTo
方法
input.CopyTo(output);
对于.NET 3.5和之前
这个框架没有任何东西可以帮助解决这个问题。 您必须手动复制内容,如下所示:
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); } }
MemoryStream有.WriteTo(outstream);
而.NET 4.0在正常stream对象上有.CopyTo。
.NET 4.0:
instream.CopyTo(outstream);
我使用下面的扩展方法。 当一个stream是一个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); }
区分“CopyStream”实现的基本问题是:
- 读缓冲区的大小
- 写的大小
- 我们可以使用多个线程(我们正在阅读的时候写作)。
这些问题的答案会导致CopyStream的实现方式大不相同,并且取决于您所拥有的streamtypes以及要优化的内容。 “最佳”实现甚至需要知道stream正在读取和写入的具体硬件。
不幸的是,没有真正简单的解决scheme。 你可以尝试这样的事情:
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();
但是如果没有什么可读的话,Stream类的不同实现可能会有不同的performance。 从本地硬盘读取文件的stream可能会阻塞,直到读取操作从磁盘读取足够的数据以填充缓冲区,并且只有在到达文件结尾时才返回更less的数据。 另一方面,即使有更多的数据要被接收,从networking读取的数据stream也可能返回更less的数据。
在使用通用解决scheme之前,请始终检查您正在使用的特定stream类的文档。
有一种方法可以更有效地执行此操作,具体取决于您正在处理的streamtypes。 如果可以将一个或两个stream转换为MemoryStream,则可以使用GetBuffer方法直接处理表示数据的字节数组。 这可以让你使用像Array.CopyTo这样的方法,它抽象掉了由fryguybob引发的所有问题。 你可以相信.NET知道复制数据的最佳方式。
如果你想要一个stream程复制到另一个尼克发布的stream是好的,但它缺less位置重置,应该是
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 }
但如果它在运行时不使用一个程序,你shpuld使用内存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
实际上,做一个stream拷贝的方式不那么严格。 但请注意,这意味着您可以将整个文件存储在内存中。 如果您正在使用数百兆字节或更多的文件,请不要试图使用它,而不要小心。
public static void CopyStream(Stream input, Stream output) { using (StreamReader reader = new StreamReader(input)) using (StreamWriter writer = new StreamWriter(output)) { writer.Write(reader.ReadToEnd()); } }
注:也可能有一些关于二进制数据和字符编码的问题。
由于没有一个答案已经涵盖了从一个stream复制到另一个stream的asynchronous方式,下面是一个模式,我已经成功地在一个端口转发应用程序中将数据从一个networkingstream复制到另一个。 它缺乏exception处理来强调模式。
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命名空间的Stream类的新“CopyTo”方法。 使用这种方法,我们可以将一个stream复制到另一个不同stream类的stream中。
这是一个例子。
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/>");
对于.NET 3.5和之前的尝试:
MemoryStream1.WriteTo(MemoryStream2);