Как скопировать содержимое одного потока в другой? - PullRequest
499 голосов
/ 23 октября 2008

Как лучше всего скопировать содержимое одного потока в другой? Есть ли для этого стандартный метод утилит?

Ответы [ 12 ]

670 голосов
/ 23 октября 2008

Начиная с .NET 4.5, существует метод Stream.CopyToAsync

input.CopyToAsync(output);

Это вернет Task, который может быть продолжен после завершения, например:

await input.CopyToAsync(output)

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

Обратите внимание, что в зависимости от того, где сделан вызов CopyToAsync, следующий код может продолжаться или не продолжаться в том же потоке, который его вызвал.

SynchronizationContext, захваченный при вызове await, определит, в каком потоке будет выполняться продолжение.

Кроме того, этот вызов (и это деталь реализации, подлежащая изменению) все еще выполняет последовательности операций чтения и записи (он просто не тратит потоки, блокирующие завершение ввода-вывода).

Начиная с .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);
    }
}

Примечание 1: Этот метод позволит вам сообщать о прогрессе (пока прочитано х байт ...)
Примечание 2: Зачем использовать фиксированный размер буфера, а не input.Length? Потому что эта длина может быть недоступна! Из документов :

Если класс, производный от Stream, не поддерживает поиск, вызовы Length, SetLength, Position и Seek выдают исключение NotSupportedException.

63 голосов
/ 22 июня 2010

MemoryStream имеет .WriteTo (outtream);

и .NET 4.0 имеет .CopyTo для обычного объекта потока.

.NET 4.0:

instream.CopyTo(outstream);
31 голосов
/ 10 августа 2009

Я использую следующие методы расширения. Они оптимизировали перегрузки для случая, когда один поток является 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);
    }
1 голос
/ 27 июля 2012

.NET Framework 4 представляет новый метод «CopyTo» для класса потоков пространства имен System.IO. Используя этот метод, мы можем скопировать один поток в другой поток другого класса потока.

Вот пример для этого.

    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/>");
1 голос
/ 10 августа 2009

Существует на самом деле менее сложный способ создания потоковой копии. Обратите внимание, однако, что это означает, что вы можете хранить весь файл в памяти. Не пытайтесь использовать это, если вы работаете с файлами, которые занимают сотни мегабайт или более, без осторожности.

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

ПРИМЕЧАНИЕ. Могут также возникнуть некоторые проблемы, касающиеся двоичных данных и кодировки символов.

1 голос
/ 23 октября 2008

Основные вопросы, которые отличают реализации «CopyStream»:

  • размер буфера чтения
  • размер записи
  • Можем ли мы использовать более одного потока (запись, пока мы читаем).

Ответы на эти вопросы приводят к совершенно разным реализациям CopyStream и зависят от того, какие потоки у вас есть и что вы пытаетесь оптимизировать. «Лучшая» реализация даже должна знать, на каком конкретном оборудовании потоки читают и записывают.

0 голосов
/ 30 апреля 2018

Легко и безопасно - создайте новый поток из оригинального источника:

    MemoryStream source = new MemoryStream(byteArray);
    MemoryStream copy = new MemoryStream(byteArray);
0 голосов
/ 29 июня 2013

Для .NET 3.5 и до того, как попробовать:

MemoryStream1.WriteTo(MemoryStream2);
0 голосов
/ 08 ноября 2011

Так как ни один из ответов не охватывал асинхронный способ копирования из одного потока в другой, вот шаблон, который я успешно использовал в приложении переадресации портов для копирования данных из одного сетевого потока в другой. В нем отсутствует обработка исключений, чтобы подчеркнуть шаблон.

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 голосов
/ 22 марта 2009

, если вы хотите, чтобы процедура копировала поток в другой, который опубликовал ник, это нормально, но отсутствует сброс позиции, это должно быть

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...