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

发布于 2024-08-09 01:15:47 字数 43 浏览 3 评论 0原文

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

花之痕靓丽 2024-08-16 01:15:47

从 .NET 4.5 开始,出现了 < code>Stream.CopyToAsync 方法

input.CopyToAsync(output);

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

await input.CopyToAsync(output)

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

请注意,这取决于对 CopyToAsync 的调用位置,后面的代码可能会也可能不会在调用它的同一线程上继续。

SynchronizationContext 是调用 await 时捕获将确定延续将在哪个线程上执行。

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

从 .NET 4.0 开始,出现了 < code>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);
    }
}

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

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

From .NET 4.5 on, there is the Stream.CopyToAsync method

input.CopyToAsync(output);

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

await input.CopyToAsync(output)

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

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

The SynchronizationContext that was captured when calling await will determine what thread the continuation will be executed on.

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).

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

input.CopyTo(output);

For .NET 3.5 and before

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:

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

日久见人心 2024-08-16 01:15:47

MemoryStream

在普通流对象上有 .WriteTo(outstream); ,.NET 4.0 有 .CopyTo

.NET 4.0:

instream.CopyTo(outstream);

MemoryStream has .WriteTo(outstream);

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

.NET 4.0:

instream.CopyTo(outstream);
°如果伤别离去 2024-08-16 01:15:47

我使用以下扩展方法。当一个流是 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);
    }

I use the following extension methods. They have optimized overloads for when one stream is a 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);
    }
ゞ记忆︶ㄣ 2024-08-16 01:15:47

.NET Framework 4 引入了 System.IO 命名空间的 Stream 类的新“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/>");

.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.

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/>");
韶华倾负 2024-08-16 01:15:47

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

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

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

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 CopySmallTextStream(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.

少钕鈤記 2024-08-16 01:15:47

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

  • 读取缓冲区的大小
  • 写入的
  • 大小我们是否可以使用多个线程(在读取时写入)。

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

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

  • 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.

乖乖 2024-08-16 01:15:47

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

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 类的不同实现可能会表现不同。从本地硬盘驱动器读取文件的流可能会阻塞,直到读取操作从磁盘读取了足够的数据来填充缓冲区,并且只有在到达文件末尾时才返回较少的数据。另一方面,即使还有更多数据需要接收,从网络读取的流也可能返回更少的数据。

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

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.

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

素罗衫 2024-08-16 01:15:47

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

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.

赢得她心 2024-08-16 01:15:47

如果您想要一个程序将流复制到其他程序,那么尼克发布的程序就可以,但它缺少位置重置,应该是,

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
}

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

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

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

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
爱要勇敢去追 2024-08-16 01:15:47

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

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

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);
}
毁梦 2024-08-16 01:15:47

对于 .NET 3.5 及之前尝试:

MemoryStream1.WriteTo(MemoryStream2);

For .NET 3.5 and before try :

MemoryStream1.WriteTo(MemoryStream2);
指尖微凉心微凉 2024-08-16 01:15:47

简单又安全 - 从原始来源创建新流:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);

Easy and safe - make new stream from original source:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
请别遗忘我 2024-08-16 01:15:47

将 Stream 复制到 MemoryStream 的问题

Stream stream = new MemoryStream();

以下代码解决了使用 CopyTo //任何需要输入流的函数 。在我的例子中,将 PDF 文件保存为流
文档.保存(流);

MemoryStream newMs = (MemoryStream)stream;

byte[] getByte = newMs.ToArray();

//注意 - 请将流放置在finally块中,而不是放置在using块中,因为它会抛出错误“流关闭时访问被拒绝”

The following code to solve the issue copy the Stream to MemoryStream using CopyTo

Stream stream = new MemoryStream();

//any function require input the stream. In mycase to save the PDF file as stream
document.Save(stream);

MemoryStream newMs = (MemoryStream)stream;

byte[] getByte = newMs.ToArray();

//Note - please dispose the stream in the finally block instead of inside using block as it will throw an error 'Access denied as the stream is closed'

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文