Как скопировать содержимое одного потока в другой?

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

Ответ 1

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

Ответ 2

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

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

.NET 4.0:

instream.CopyTo(outstream);

Ответ 3

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 6

.NET Framework 4 представляет новый метод "CopyTo" класса Stream Class пространства 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/>");

Ответ 7

К сожалению, нет простого решения. Вы можете попробовать что-то вроде этого:

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 может вести себя по-другому, если читать нечего. Поток, считывающий файл с локального жесткого диска, вероятно, будет заблокирован до тех пор, пока операция чтения не будет считывать достаточное количество данных с диска, чтобы заполнить буфер, и только вернуть меньше данных, если оно достигнет конца файла. С другой стороны, чтение потока из сети может возвращать меньше данных, даже если есть больше данных, которые нужно получить.

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

Ответ 8

Возможно, есть способ сделать это более эффективно, в зависимости от того, с каким потоком вы работаете. Если вы можете конвертировать один или оба ваших потока в MemoryStream, вы можете использовать метод GetBuffer для работы напрямую с массивом байтов, представляющим ваши данные. Это позволяет использовать такие методы, как Array.CopyTo, которые абстрагируют все проблемы, поднятые fryguybob. Вы можете просто доверять .NET, чтобы узнать оптимальный способ копирования данных.

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

Для .NET 3.5 и перед попыткой:

MemoryStream1.WriteTo(MemoryStream2);

Ответ 12

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

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