首页 文章

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

提问于
浏览
485

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

12 回答

  • 1

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

    input.CopyToAsync(output);
    

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

    await input.CopyToAsync(output)
    
    // Code from here on will be run in a continuation.
    

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

    调用await时捕获的SynchronizationContext将确定将继续执行哪个线程 .

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

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

    input.CopyTo(output);
    

    For .NET 3.5 and before

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

    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 ?因为该长度可能无法使用!来自docs

    如果从Stream派生的类不支持搜索,则调用Length,SetLength,Position和Seek会抛出NotSupportedException .

  • 647

    MemoryStream有.WriteTo(outstream);

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

    .NET 4.0:

    instream.CopyTo(outstream);
    
  • 1

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

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

    • 读取缓冲区的大小

    • 写入的大小

    • 我们可以使用多个线程(在我们阅读时写作) .

    这些问题的答案导致CopyStream的实现差异很大,并且取决于您拥有的流类型以及您要优化的内容 . “最佳”实现甚至需要知道流正在读取和写入的具体硬件 .

  • 0

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

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

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

  • 0

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

    "); Response.Write(string.Format("MemoryStream Content length: {0}", objMemoryStream.Length.ToString())); Response.Write("

    ");
  • 0

    不幸的是,没有真正简单的解决方案 . 你可以试试这样的东西:

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

    在使用通用解决方案之前,请始终检查您正在使用的特定流类的文档 .

  • 61

    可能有一种方法可以更有效地执行此操作,具体取决于您正在使用的流类型 . 如果可以将一个或两个流转换为MemoryStream,则可以使用GetBuffer方法直接处理表示数据的字节数组 . 这允许您使用像Array.CopyTo这样的方法,它抽象出fryguybob引发的所有问题 . 您可以信任.NET以了解复制数据的最佳方式 .

  • 1

    如果你想要一个procdure将一个流复制到其他那个尼克发布的那个很好但是它错过了位置重置,它应该是

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

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

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

    对于.NET 3.5和尝试之前:

    MemoryStream1.WriteTo(MemoryStream2);
    
  • 0

    简单安全 - 从原始来源制作新流:

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

相关问题