Может быть, еще более важно на этом этапе, как вы копируете содержимое «вразумительно», что означает, что он копирует только исходный поток, когда что-то потребляет целевой поток ...?
Это вернет a, Taskкоторый можно продолжить после завершения, например, так:
await input.CopyToAsync(output)// Code from here on will be run in a continuation.
Обратите внимание, что в зависимости от того, где CopyToAsyncсделан вызов , следующий код может продолжаться или не продолжаться в том же потоке, который его вызвал.
То, SynchronizationContextчто было перехвачено при вызове await, определит, в каком потоке будет выполняться продолжение.
Кроме того, этот вызов (и это деталь реализации, подлежащая изменению) все еще выполняет чтение и запись последовательности (он просто не тратит потоки, блокирующие завершение ввода-вывода).
Примечание 1: Этот метод позволит вам сообщать о прогрессе (пока прочитано х байтов ...)
Примечание 2: Почему используется фиксированный размер буфера, а не input.Length? Потому что эта длина может быть недоступна! Из документов :
Если класс, производный от Stream, не поддерживает поиск, вызовы Length, SetLength, Position и Seek выдают исключение NotSupportedException.
Обратите внимание, что это не самый быстрый способ сделать это. В приведенном фрагменте кода необходимо дождаться завершения записи, прежде чем будет прочитан новый блок. При асинхронном чтении и записи это ожидание исчезнет. В некоторых ситуациях это сделает копию в два раза быстрее. Однако это сделает код намного более сложным, поэтому, если скорость не является проблемой, сделайте это простым и используйте этот простой цикл. Этот вопрос о StackOverflow имеет некоторый код, который иллюстрирует асинхронное чтение / запись: stackoverflow.com/questions/1540658/… С уважением, Себастьян
Себастьян М
16
FWIW, в моем тестировании я обнаружил, что 4096 на самом деле быстрее, чем 32K. Как-то связано с тем, как CLR распределяет порции по определенному размеру. Из-за этого .NET-реализация Stream.CopyTo, по-видимому, использует 4096.
Джефф
1
Если вы хотите знать, как реализован CopyToAsync, или вносить изменения, как я (мне нужно было указать максимальное количество байт для копирования), тогда он доступен как CopyStreamToStreamAsync в «Образцах для параллельного программирования с .NET Framework» code.msdn. .microsoft.com / ParExtSamples
Майкл,
1
FIY, оптимальный размер буфера из 81920байтов, а не32768
Я не вижу много примеров в Интернете с использованием этих методов. Это потому, что они довольно новые или есть некоторые ограничения?
GeneS
3
Это потому, что они являются новыми в .NET 4.0. Stream.CopyTo () в основном делает то же самое для цикла, что и утвержденный ответ, с некоторыми дополнительными проверками работоспособности. Размер буфера по умолчанию - 4096, но есть и перегрузка, чтобы указать больший.
Майкл Иденфилд
9
Поток нужно перематывать после копирования: instream.Position = 0;
Драйкос
6
Помимо перемотки входного потока, я также обнаружил необходимость перематывать выходной поток: outstream.Position = 0;
Джон
32
Я использую следующие методы расширения. Они оптимизировали перегрузки для случая, когда один поток является MemoryStream.
Основные вопросы, которые отличают реализации «CopyStream»:
размер буфера чтения
размер записи
Можем ли мы использовать более одного потока (писать, пока мы читаем).
Ответы на эти вопросы приводят к совершенно разным реализациям CopyStream и зависят от того, какие потоки у вас есть и что вы пытаетесь оптимизировать. «Лучшая» реализация даже должна знать, на каком конкретном оборудовании потоки читают и записывают данные.
... или лучшая реализация может иметь перегрузки, позволяющие вам указать размер буфера, размер записи и разрешены ли потоки?
MarkJ
1
На самом деле существует менее жесткий способ создания потоковой копии. Обратите внимание, однако, что это означает, что вы можете хранить весь файл в памяти. Не пытайтесь использовать это, если вы работаете с файлами, которые занимают сотни мегабайт или более, без осторожности.
publicstaticvoidCopyStream(Stream input,Stream output){
using (StreamReader reader =newStreamReader(input))
using (StreamWriter writer =newStreamWriter(output)){
writer.Write(reader.ReadToEnd());}}
ПРИМЕЧАНИЕ. Могут также возникнуть некоторые проблемы, касающиеся двоичных данных и кодировки символов.
Конструктор по умолчанию для StreamWriter создает поток UTF8 без спецификации ( msdn.microsoft.com/en-us/library/fysy0a4b.aspx ), поэтому нет опасности возникновения проблем с кодированием. Двоичные данные почти наверняка не должны копироваться таким образом.
kͩeͣmͮpͥ ͩ
14
Можно легко утверждать, что загрузка «всего файла в память» вряд ли считается «менее жесткой».
Seph
я получаю исключение из-за этого
ColacX
Это не поток в поток. reader.ReadToEnd()помещает все в оперативную память
Бижан
1
.NET Framework 4 представляет новый метод «CopyTo» для класса потока пространства имен System.IO. Используя этот метод, мы можем скопировать один поток в другой поток другого класса потока.
Но проблема в том, что другая реализация класса Stream может вести себя по-разному, если нечего читать. Поток, считывающий файл с локального жесткого диска, вероятно, будет блокироваться до тех пор, пока операция чтения не прочитает достаточно данных с диска, чтобы заполнить буфер, и вернет меньше данных, только если достигнет конца файла. С другой стороны, потоковое чтение из сети может вернуть меньше данных, даже если осталось больше данных для получения.
Всегда проверяйте документацию определенного класса потока, который вы используете, прежде чем использовать универсальное решение.
Здесь будет работать общее решение - хороший ответ Ника. Размер буфера, конечно, произвольный выбор, но 32K звучит разумно. Я думаю, что решение Ника - правильно не закрывать потоки - оставьте это владельцу.
Джон Скит
0
Может быть способ сделать это более эффективно, в зависимости от того, с каким потоком вы работаете. Если вы можете преобразовать один или оба ваших потока в MemoryStream, вы можете использовать метод GetBuffer для работы непосредственно с байтовым массивом, представляющим ваши данные. Это позволяет вам использовать такие методы, как Array.CopyTo, которые абстрагируют все проблемы, поднятые fryguybob. Вы можете просто доверять .NET, чтобы знать оптимальный способ копирования данных.
если вы хотите, чтобы процедура копировала поток в другой, который отправил ник, это нормально, но в нем отсутствует сброс позиции, это должно быть
publicstaticvoidCopyStream(Stream input,Stream output){byte[] buffer =newbyte[32768];longTempPos= 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 =newMemoryStream();byte[] buffer =newbyte[32768];// or you specify the size you want of your bufferlongTempPos= 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
Поскольку ни один из ответов не охватывал асинхронный способ копирования из одного потока в другой, здесь приведен шаблон, который я успешно использовал в приложении переадресации портов для копирования данных из одного сетевого потока в другой. В нем отсутствует обработка исключений, чтобы подчеркнуть шаблон.
constint BUFFER_SIZE =4096;staticbyte[] bufferForRead =newbyte[BUFFER_SIZE];staticbyte[] bufferForWrite =newbyte[BUFFER_SIZE];staticStream sourceStream =newMemoryStream();staticStream destinationStream =newMemoryStream();staticvoidMain(string[] args){// Initial read from source stream
sourceStream.BeginRead(bufferForRead,0, BUFFER_SIZE,BeginReadCallback,null);}privatestaticvoidBeginReadCallback(IAsyncResult asyncRes){// Finish reading from source streamint bytesRead = sourceStream.EndRead(asyncRes);// Make a copy of the buffer as we'll start another read immediatelyArray.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);}privatestaticvoidBeginWriteCallback(IAsyncResult asyncRes){// Finish writing to destination stream
destinationStream.EndWrite(asyncRes);}
Конечно, если второе чтение завершается перед первой записью, то вы будете перезаписывать содержимое bufferForWrite с первого чтения, прежде чем оно будет записано.
Ответы:
Начиная с .NET 4.5 существует
Stream.CopyToAsync
методЭто вернет a,
Task
который можно продолжить после завершения, например, так:Обратите внимание, что в зависимости от того, где
CopyToAsync
сделан вызов , следующий код может продолжаться или не продолжаться в том же потоке, который его вызвал.То,
SynchronizationContext
что было перехвачено при вызовеawait
, определит, в каком потоке будет выполняться продолжение.Кроме того, этот вызов (и это деталь реализации, подлежащая изменению) все еще выполняет чтение и запись последовательности (он просто не тратит потоки, блокирующие завершение ввода-вывода).
Начиная с .NET 4.0, есть
Stream.CopyTo
методДля .NET 3.5 и раньше
Ничто не запечено в рамках, чтобы помочь с этим; Вы должны скопировать содержимое вручную, например так:
Примечание 1: Этот метод позволит вам сообщать о прогрессе (пока прочитано х байтов ...)
Примечание 2: Почему используется фиксированный размер буфера, а не
input.Length
? Потому что эта длина может быть недоступна! Из документов :источник
81920
байтов, а не32768
MemoryStream имеет .WriteTo (outstream);
и .NET 4.0 имеет .CopyTo для обычного объекта потока.
.NET 4.0:
источник
Я использую следующие методы расширения. Они оптимизировали перегрузки для случая, когда один поток является MemoryStream.
источник
Основные вопросы, которые отличают реализации «CopyStream»:
Ответы на эти вопросы приводят к совершенно разным реализациям CopyStream и зависят от того, какие потоки у вас есть и что вы пытаетесь оптимизировать. «Лучшая» реализация даже должна знать, на каком конкретном оборудовании потоки читают и записывают данные.
источник
На самом деле существует менее жесткий способ создания потоковой копии. Обратите внимание, однако, что это означает, что вы можете хранить весь файл в памяти. Не пытайтесь использовать это, если вы работаете с файлами, которые занимают сотни мегабайт или более, без осторожности.
ПРИМЕЧАНИЕ. Могут также возникнуть некоторые проблемы, касающиеся двоичных данных и кодировки символов.
источник
reader.ReadToEnd()
помещает все в оперативную память.NET Framework 4 представляет новый метод «CopyTo» для класса потока пространства имен System.IO. Используя этот метод, мы можем скопировать один поток в другой поток другого класса потока.
Вот пример для этого.
источник
CopyToAsync()
приветствуется.К сожалению, нет действительно простого решения. Вы можете попробовать что-то вроде этого:
Но проблема в том, что другая реализация класса Stream может вести себя по-разному, если нечего читать. Поток, считывающий файл с локального жесткого диска, вероятно, будет блокироваться до тех пор, пока операция чтения не прочитает достаточно данных с диска, чтобы заполнить буфер, и вернет меньше данных, только если достигнет конца файла. С другой стороны, потоковое чтение из сети может вернуть меньше данных, даже если осталось больше данных для получения.
Всегда проверяйте документацию определенного класса потока, который вы используете, прежде чем использовать универсальное решение.
источник
Может быть способ сделать это более эффективно, в зависимости от того, с каким потоком вы работаете. Если вы можете преобразовать один или оба ваших потока в MemoryStream, вы можете использовать метод GetBuffer для работы непосредственно с байтовым массивом, представляющим ваши данные. Это позволяет вам использовать такие методы, как Array.CopyTo, которые абстрагируют все проблемы, поднятые fryguybob. Вы можете просто доверять .NET, чтобы знать оптимальный способ копирования данных.
источник
если вы хотите, чтобы процедура копировала поток в другой, который отправил ник, это нормально, но в нем отсутствует сброс позиции, это должно быть
но если он во время выполнения не использует процедуру, вы должны использовать поток памяти
источник
Поскольку ни один из ответов не охватывал асинхронный способ копирования из одного потока в другой, здесь приведен шаблон, который я успешно использовал в приложении переадресации портов для копирования данных из одного сетевого потока в другой. В нем отсутствует обработка исключений, чтобы подчеркнуть шаблон.
источник
Для .NET 3.5 и до этого попробуйте:
источник
Легко и безопасно - создайте новый поток из оригинального источника:
источник