简体   繁体   English

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

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

What is the best way to copy the contents of one stream to another?将一个 stream 的内容复制到另一个的最佳方法是什么? Is there a standard utility method for this?有没有标准的实用方法?

From .NET 4.5 on, there is the Stream.CopyToAsync method从 .NET 4.5 开始,有Stream.CopyToAsync方法

input.CopyToAsync(output);

This will return a Task that 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 CopyToAsync is made, the code that follows may or may not continue on the same thread that called it.请注意,根据调用CopyToAsync位置, CopyToAsync的代码可能会或可能不会在调用它的同一线程上继续。

The SynchronizationContext that was captured when calling await will determine what thread the continuation will be executed on.调用await时捕获的SynchronizationContext将确定将在哪个线程上执行延续。

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.CopyTo method从 .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 ...)注 1:此方法将允许您报告进度(到目前为止已读取 x 字节...)
Note 2: Why use a fixed buffer size and not input.Length ?注 2:为什么使用固定缓冲区大小而不是input.Length Because that Length may not be available!因为那个 Length 可能不可用! From the docs :文档

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。

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

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

.NET Framework 4 introduce new "CopyTo" method of Stream Class of System.IO namespace. .NET Framework 4 引入了 System.IO 命名空间的 Stream 类的新“CopyTo”方法。 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/>");

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.注意:二进制数据和字符编码也可能存在一些问题。

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.这些问题的答案导致了 CopyStream 的截然不同的实现,并且取决于您拥有的流类型以及您要优化的流。 The "best" implementation would even need to know what specific hardware the streams were reading and writing to. “最佳”实现甚至需要知道流正在读取和写入的特定硬件。

对于 .NET 3.5 和尝试之前:

MemoryStream1.WriteTo(MemoryStream2);

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.但是,如果没有可读取的内容,那么 Stream 类的不同实现可能会表现出不同的问题。 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.在使用通用解决方案之前,请务必检查您正在使用的特定流类的文档。

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.如果您可以将一个或两个流转换为 MemoryStream,则可以使用 GetBuffer 方法直接处理表示数据的字节数组。 This lets you use methods like Array.CopyTo, which abstract away all the issues raised by fryguybob.这让您可以使用 Array.CopyTo 之类的方法,这些方法可以抽象出 fryguybob 提出的所有问题。 You can just trust .NET to know the optimal way to copy the data.您可以相信 .NET 知道复制数据的最佳方式。

Easy and safe - make new stream from original source:简单安全 - 从原始来源制作新流:

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

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

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

The following code to solve the issue copy the Stream to MemoryStream using CopyTo以下代码解决问题,使用 CopyTo 将 Stream 复制到 MemoryStream

Stream stream = new MemoryStream();

//any function require input the stream. //任何函数都需要输入流。 In mycase to save the PDF file as stream document.Save(stream);在 mycase 中将 PDF 文件保存为流 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' //注意 - 请在 finally 块中而不是在 using 块中处理流,因为它会抛出错误“访问被拒绝,因为流关闭”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用AWS api将一个目录的内容复制到另一个目录? - How can I copy contents of one directory to another with the AWS api? 如何将所有数据从一个单元格复制到另一个单元格? - How do I copy all data from one cell to another? 如何将文件从一个目录复制到另一目录? - How do i copy files from one directory to another directory? 如何将属性从一个HttpWebRequest复制到另一个? - How do I Copy Properties From one HttpWebRequest to Another? 如何将一个文本文件的内容复制到另一个长度减小的文件中? - How to copy contents of one text file to another with reduced length? 如何将一个Outlook文件夹的内容复制到另一个Outlook文件夹 - How to copy the contents of one Outlook folder to another Outlook Folder 如何将SecureString的内容写入Response流? - How do I write the contents of a SecureString to the Response stream? 如何将字符串的内容复制到C#中的剪贴板? - How do I copy the contents of a String to the clipboard in C#? 如何将控件从一个面板复制到另一个面板并保持对齐? - How do I copy controls from one panel to another and keep aligned? 如何将一个列表复制到另一个具有 object 和 C# 附加属性的列表? (没有foreach) - How do I copy one list to another that has an object with additional properties in C# ? (without a foreach)
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM